Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/gmp/ChromiumCDMChild.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "ChromiumCDMChild.h"
7
#include "GMPContentChild.h"
8
#include "WidevineUtils.h"
9
#include "WidevineFileIO.h"
10
#include "WidevineVideoFrame.h"
11
#include "GMPLog.h"
12
#include "GMPPlatform.h"
13
#include "mozilla/Unused.h"
14
#include "nsPrintfCString.h"
15
#include "base/time.h"
16
#include "GMPUtils.h"
17
#include "mozilla/ScopeExit.h"
18
#include "CDMStorageIdProvider.h"
19
20
namespace mozilla {
21
namespace gmp {
22
23
ChromiumCDMChild::ChromiumCDMChild(GMPContentChild* aPlugin)
24
  : mPlugin(aPlugin)
25
0
{
26
0
  MOZ_ASSERT(IsOnMessageLoopThread());
27
0
  GMP_LOG("ChromiumCDMChild:: ctor this=%p", this);
28
0
}
29
30
void
31
ChromiumCDMChild::Init(cdm::ContentDecryptionModule_10* aCDM,
32
                       const nsCString& aStorageId)
33
0
{
34
0
  MOZ_ASSERT(IsOnMessageLoopThread());
35
0
  mCDM = aCDM;
36
0
  MOZ_ASSERT(mCDM);
37
0
  mStorageId = aStorageId;
38
0
}
39
40
void
41
ChromiumCDMChild::TimerExpired(void* aContext)
42
0
{
43
0
  MOZ_ASSERT(IsOnMessageLoopThread());
44
0
  GMP_LOG("ChromiumCDMChild::TimerExpired(context=0x%p)", aContext);
45
0
  if (mCDM) {
46
0
    mCDM->TimerExpired(aContext);
47
0
  }
48
0
}
49
50
class CDMShmemBuffer : public CDMBuffer
51
{
52
public:
53
  CDMShmemBuffer(ChromiumCDMChild* aProtocol, ipc::Shmem aShmem)
54
    : mProtocol(aProtocol)
55
    , mSize(aShmem.Size<uint8_t>())
56
    , mShmem(aShmem)
57
0
  {
58
0
    GMP_LOG("CDMShmemBuffer(size=%" PRIu32 ") created", Size());
59
0
    // Note: Chrome initializes the size of a buffer to it capacity. We do the same.
60
0
  }
61
62
  CDMShmemBuffer(ChromiumCDMChild* aProtocol,
63
                 ipc::Shmem aShmem,
64
                 WidevineBuffer* aLocalBuffer)
65
    : CDMShmemBuffer(aProtocol, aShmem)
66
0
  {
67
0
    MOZ_ASSERT(aLocalBuffer->Size() == Size());
68
0
    memcpy(Data(),
69
0
           aLocalBuffer->Data(),
70
0
           std::min<uint32_t>(aLocalBuffer->Size(), Size()));
71
0
  }
72
73
  ~CDMShmemBuffer() override
74
0
  {
75
0
    GMP_LOG("CDMShmemBuffer(size=%" PRIu32 ") destructed writable=%d",
76
0
            Size(),
77
0
            mShmem.IsWritable());
78
0
    if (mShmem.IsWritable()) {
79
0
      // The shmem wasn't extracted to send its data back up to the parent process,
80
0
      // so we can reuse the shmem.
81
0
      mProtocol->GiveBuffer(std::move(mShmem));
82
0
    }
83
0
  }
84
85
  void Destroy() override
86
0
  {
87
0
    GMP_LOG("CDMShmemBuffer::Destroy(size=%" PRIu32 ")", Size());
88
0
    delete this;
89
0
  }
90
0
  uint32_t Capacity() const override { return mShmem.Size<uint8_t>(); }
91
92
0
  uint8_t* Data() override { return mShmem.get<uint8_t>(); }
93
94
  void SetSize(uint32_t aSize) override
95
0
  {
96
0
    MOZ_ASSERT(aSize <= Capacity());
97
0
    // Note: We can't use the shmem's size member after ExtractShmem(),
98
0
    // has been called, so we track the size exlicitly so that we can use
99
0
    // Size() in logging after we've called ExtractShmem().
100
0
    GMP_LOG("CDMShmemBuffer::SetSize(size=%" PRIu32 ")", Size());
101
0
    mSize = aSize;
102
0
  }
103
104
0
  uint32_t Size() const override { return mSize; }
105
106
  ipc::Shmem ExtractShmem()
107
0
  {
108
0
    ipc::Shmem shmem = mShmem;
109
0
    mShmem = ipc::Shmem();
110
0
    return shmem;
111
0
  }
112
113
0
  CDMShmemBuffer* AsShmemBuffer() override { return this; }
114
115
private:
116
  RefPtr<ChromiumCDMChild> mProtocol;
117
  uint32_t mSize;
118
  mozilla::ipc::Shmem mShmem;
119
  CDMShmemBuffer(const CDMShmemBuffer&);
120
  void operator=(const CDMShmemBuffer&);
121
};
122
123
static nsCString
124
ToString(const nsTArray<ipc::Shmem>& aBuffers)
125
0
{
126
0
  nsCString s;
127
0
  for (const ipc::Shmem& shmem : aBuffers) {
128
0
    if (!s.IsEmpty()) {
129
0
      s.AppendLiteral(",");
130
0
    }
131
0
    s.AppendInt(static_cast<uint32_t>(shmem.Size<uint8_t>()));
132
0
  }
133
0
  return s;
134
0
}
135
136
cdm::Buffer*
137
ChromiumCDMChild::Allocate(uint32_t aCapacity)
138
0
{
139
0
  GMP_LOG("ChromiumCDMChild::Allocate(capacity=%" PRIu32 ") bufferSizes={%s}",
140
0
          aCapacity,
141
0
          ToString(mBuffers).get());
142
0
  MOZ_ASSERT(IsOnMessageLoopThread());
143
0
144
0
  if (mBuffers.IsEmpty()) {
145
0
    Unused << SendIncreaseShmemPoolSize();
146
0
  }
147
0
148
0
  // Find the shmem with the least amount of wasted space if we were to
149
0
  // select it for this sized allocation. We need to do this because shmems
150
0
  // for decrypted audio as well as video frames are both stored in this
151
0
  // list, and we don't want to use the video frame shmems for audio samples.
152
0
  const size_t invalid = std::numeric_limits<size_t>::max();
153
0
  size_t best = invalid;
154
0
  auto wastedSpace = [this, aCapacity](size_t index) {
155
0
    return mBuffers[index].Size<uint8_t>() - aCapacity;
156
0
  };
157
0
  for (size_t i = 0; i < mBuffers.Length(); i++) {
158
0
    if (mBuffers[i].Size<uint8_t>() >= aCapacity &&
159
0
        (best == invalid || wastedSpace(i) < wastedSpace(best))) {
160
0
      best = i;
161
0
    }
162
0
  }
163
0
  if (best == invalid) {
164
0
    // The parent process should have bestowed upon us a shmem of appropriate
165
0
    // size, but did not! Do a "dive and catch", and create an non-shared
166
0
    // memory buffer. The parent will detect this and send us an extra shmem
167
0
    // so future frames can be in shmems, i.e. returned on the fast path.
168
0
    return new WidevineBuffer(aCapacity);
169
0
  }
170
0
  ipc::Shmem shmem = mBuffers[best];
171
0
  mBuffers.RemoveElementAt(best);
172
0
  return new CDMShmemBuffer(this, shmem);
173
0
}
174
175
void
176
ChromiumCDMChild::SetTimer(int64_t aDelayMs, void* aContext)
177
0
{
178
0
  MOZ_ASSERT(IsOnMessageLoopThread());
179
0
  GMP_LOG("ChromiumCDMChild::SetTimer(delay=%" PRId64 ", context=0x%p)",
180
0
          aDelayMs,
181
0
          aContext);
182
0
  RefPtr<ChromiumCDMChild> self(this);
183
0
  SetTimerOnMainThread(NewGMPTask([self, aContext]() {
184
0
    self->TimerExpired(aContext);
185
0
  }), aDelayMs);
186
0
}
187
188
cdm::Time
189
ChromiumCDMChild::GetCurrentWallTime()
190
0
{
191
0
  return base::Time::Now().ToDoubleT();
192
0
}
193
194
template <typename MethodType, typename... ParamType>
195
void
196
ChromiumCDMChild::CallMethod(MethodType aMethod, ParamType&&... aParams)
197
0
{
198
0
  MOZ_ASSERT(IsOnMessageLoopThread());
199
0
  // Avoid calling member function after destroy.
200
0
  if (!mDestroyed) {
201
0
    Unused << (this->*aMethod)(std::forward<ParamType>(aParams)...);
202
0
  }
203
0
}
Unexecuted instantiation: void mozilla::gmp::ChromiumCDMChild::CallMethod<bool (mozilla::gmp::PChromiumCDMChild::*)(unsigned int const&, unsigned int const&), unsigned int&, unsigned int>(bool (mozilla::gmp::PChromiumCDMChild::*)(unsigned int const&, unsigned int const&), unsigned int&, unsigned int&&)
Unexecuted instantiation: void mozilla::gmp::ChromiumCDMChild::CallMethod<bool (mozilla::gmp::PChromiumCDMChild::*)(unsigned int const&, unsigned int const&), unsigned int const&, unsigned int const&>(bool (mozilla::gmp::PChromiumCDMChild::*)(unsigned int const&, unsigned int const&), unsigned int const&, unsigned int const&)
Unexecuted instantiation: void mozilla::gmp::ChromiumCDMChild::CallMethod<bool (mozilla::gmp::ChromiumCDMChild::*)(unsigned int, nsTString<char> const&), unsigned int&, nsTString<char> >(bool (mozilla::gmp::ChromiumCDMChild::*)(unsigned int, nsTString<char> const&), unsigned int&, nsTString<char>&&)
Unexecuted instantiation: void mozilla::gmp::ChromiumCDMChild::CallMethod<bool (mozilla::gmp::ChromiumCDMChild::*)(unsigned int, nsTString<char> const&), unsigned int const&, nsTString<char> const&>(bool (mozilla::gmp::ChromiumCDMChild::*)(unsigned int, nsTString<char> const&), unsigned int const&, nsTString<char> const&)
Unexecuted instantiation: void mozilla::gmp::ChromiumCDMChild::CallMethod<bool (mozilla::gmp::PChromiumCDMChild::*)(unsigned int const&), unsigned int&>(bool (mozilla::gmp::PChromiumCDMChild::*)(unsigned int const&), unsigned int&)
Unexecuted instantiation: void mozilla::gmp::ChromiumCDMChild::CallMethod<bool (mozilla::gmp::PChromiumCDMChild::*)(unsigned int const&), unsigned int const&>(bool (mozilla::gmp::PChromiumCDMChild::*)(unsigned int const&), unsigned int const&)
Unexecuted instantiation: void mozilla::gmp::ChromiumCDMChild::CallMethod<bool (mozilla::gmp::PChromiumCDMChild::*)(unsigned int const&, unsigned int const&, unsigned int const&, nsTString<char> const&), unsigned int&, unsigned int, unsigned int&, nsTString<char> >(bool (mozilla::gmp::PChromiumCDMChild::*)(unsigned int const&, unsigned int const&, unsigned int const&, nsTString<char> const&), unsigned int&, unsigned int&&, unsigned int&, nsTString<char>&&)
Unexecuted instantiation: void mozilla::gmp::ChromiumCDMChild::CallMethod<bool (mozilla::gmp::PChromiumCDMChild::*)(unsigned int const&, unsigned int const&, unsigned int const&, nsTString<char> const&), unsigned int const&, unsigned int const&, unsigned int const&, nsTString<char> const&>(bool (mozilla::gmp::PChromiumCDMChild::*)(unsigned int const&, unsigned int const&, unsigned int const&, nsTString<char> const&), unsigned int const&, unsigned int const&, unsigned int const&, nsTString<char> const&)
Unexecuted instantiation: void mozilla::gmp::ChromiumCDMChild::CallMethod<bool (mozilla::gmp::PChromiumCDMChild::*)(nsTString<char> const&, unsigned int const&, nsTArray<unsigned char> const&), nsTString<char>, unsigned int, nsTArray<unsigned char>&>(bool (mozilla::gmp::PChromiumCDMChild::*)(nsTString<char> const&, unsigned int const&, nsTArray<unsigned char> const&), nsTString<char>&&, unsigned int&&, nsTArray<unsigned char>&)
Unexecuted instantiation: void mozilla::gmp::ChromiumCDMChild::CallMethod<bool (mozilla::gmp::PChromiumCDMChild::*)(nsTString<char> const&, unsigned int const&, nsTArray<unsigned char> const&), nsTString<char> const&, unsigned int const&, nsTArray<unsigned char> const&>(bool (mozilla::gmp::PChromiumCDMChild::*)(nsTString<char> const&, unsigned int const&, nsTArray<unsigned char> const&), nsTString<char> const&, unsigned int const&, nsTArray<unsigned char> const&)
Unexecuted instantiation: void mozilla::gmp::ChromiumCDMChild::CallMethod<bool (mozilla::gmp::PChromiumCDMChild::*)(nsTString<char> const&, nsTArray<mozilla::gmp::CDMKeyInformation> const&), nsTString<char>, nsTArray<mozilla::gmp::CDMKeyInformation>&>(bool (mozilla::gmp::PChromiumCDMChild::*)(nsTString<char> const&, nsTArray<mozilla::gmp::CDMKeyInformation> const&), nsTString<char>&&, nsTArray<mozilla::gmp::CDMKeyInformation>&)
Unexecuted instantiation: void mozilla::gmp::ChromiumCDMChild::CallMethod<bool (mozilla::gmp::PChromiumCDMChild::*)(nsTString<char> const&, nsTArray<mozilla::gmp::CDMKeyInformation> const&), nsTString<char> const&, nsTArray<mozilla::gmp::CDMKeyInformation> const&>(bool (mozilla::gmp::PChromiumCDMChild::*)(nsTString<char> const&, nsTArray<mozilla::gmp::CDMKeyInformation> const&), nsTString<char> const&, nsTArray<mozilla::gmp::CDMKeyInformation> const&)
Unexecuted instantiation: void mozilla::gmp::ChromiumCDMChild::CallMethod<bool (mozilla::gmp::PChromiumCDMChild::*)(nsTString<char> const&, double const&), nsTString<char>, double&>(bool (mozilla::gmp::PChromiumCDMChild::*)(nsTString<char> const&, double const&), nsTString<char>&&, double&)
Unexecuted instantiation: void mozilla::gmp::ChromiumCDMChild::CallMethod<bool (mozilla::gmp::PChromiumCDMChild::*)(nsTString<char> const&, double const&), nsTString<char> const&, double const&>(bool (mozilla::gmp::PChromiumCDMChild::*)(nsTString<char> const&, double const&), nsTString<char> const&, double const&)
Unexecuted instantiation: void mozilla::gmp::ChromiumCDMChild::CallMethod<bool (mozilla::gmp::PChromiumCDMChild::*)(nsTString<char> const&), nsTString<char> >(bool (mozilla::gmp::PChromiumCDMChild::*)(nsTString<char> const&), nsTString<char>&&)
Unexecuted instantiation: void mozilla::gmp::ChromiumCDMChild::CallMethod<bool (mozilla::gmp::PChromiumCDMChild::*)(nsTString<char> const&), nsTString<char> const&>(bool (mozilla::gmp::PChromiumCDMChild::*)(nsTString<char> const&), nsTString<char> const&)
204
205
template<typename MethodType, typename... ParamType>
206
void
207
ChromiumCDMChild::CallOnMessageLoopThread(const char* const aName,
208
                                          MethodType aMethod,
209
                                          ParamType&&... aParams)
210
0
{
211
0
  if (IsOnMessageLoopThread()) {
212
0
    CallMethod(aMethod, std::forward<ParamType>(aParams)...);
213
0
  } else {
214
0
    auto m = &ChromiumCDMChild::CallMethod<
215
0
        decltype(aMethod), const typename RemoveReference<ParamType>::Type&...>;
216
0
    RefPtr<mozilla::Runnable> t =
217
0
      NewRunnableMethod<decltype(aMethod),
218
0
                        const typename RemoveReference<ParamType>::Type...>(
219
0
                        aName,
220
0
                        this,
221
0
                        m,
222
0
                        aMethod,
223
0
                        std::forward<ParamType>(aParams)...);
224
0
    mPlugin->GMPMessageLoop()->PostTask(t.forget());
225
0
  }
226
0
}
Unexecuted instantiation: void mozilla::gmp::ChromiumCDMChild::CallOnMessageLoopThread<bool (mozilla::gmp::PChromiumCDMChild::*)(unsigned int const&, unsigned int const&), unsigned int&, unsigned int>(char const*, bool (mozilla::gmp::PChromiumCDMChild::*)(unsigned int const&, unsigned int const&), unsigned int&, unsigned int&&)
Unexecuted instantiation: void mozilla::gmp::ChromiumCDMChild::CallOnMessageLoopThread<bool (mozilla::gmp::ChromiumCDMChild::*)(unsigned int, nsTString<char> const&), unsigned int&, nsTString<char> >(char const*, bool (mozilla::gmp::ChromiumCDMChild::*)(unsigned int, nsTString<char> const&), unsigned int&, nsTString<char>&&)
Unexecuted instantiation: void mozilla::gmp::ChromiumCDMChild::CallOnMessageLoopThread<bool (mozilla::gmp::PChromiumCDMChild::*)(unsigned int const&), unsigned int&>(char const*, bool (mozilla::gmp::PChromiumCDMChild::*)(unsigned int const&), unsigned int&)
Unexecuted instantiation: void mozilla::gmp::ChromiumCDMChild::CallOnMessageLoopThread<bool (mozilla::gmp::PChromiumCDMChild::*)(unsigned int const&, unsigned int const&, unsigned int const&, nsTString<char> const&), unsigned int&, unsigned int, unsigned int&, nsTString<char> >(char const*, bool (mozilla::gmp::PChromiumCDMChild::*)(unsigned int const&, unsigned int const&, unsigned int const&, nsTString<char> const&), unsigned int&, unsigned int&&, unsigned int&, nsTString<char>&&)
Unexecuted instantiation: void mozilla::gmp::ChromiumCDMChild::CallOnMessageLoopThread<bool (mozilla::gmp::PChromiumCDMChild::*)(nsTString<char> const&, unsigned int const&, nsTArray<unsigned char> const&), nsTString<char>, unsigned int, nsTArray<unsigned char>&>(char const*, bool (mozilla::gmp::PChromiumCDMChild::*)(nsTString<char> const&, unsigned int const&, nsTArray<unsigned char> const&), nsTString<char>&&, unsigned int&&, nsTArray<unsigned char>&)
Unexecuted instantiation: void mozilla::gmp::ChromiumCDMChild::CallOnMessageLoopThread<bool (mozilla::gmp::PChromiumCDMChild::*)(nsTString<char> const&, nsTArray<mozilla::gmp::CDMKeyInformation> const&), nsTString<char>, nsTArray<mozilla::gmp::CDMKeyInformation>&>(char const*, bool (mozilla::gmp::PChromiumCDMChild::*)(nsTString<char> const&, nsTArray<mozilla::gmp::CDMKeyInformation> const&), nsTString<char>&&, nsTArray<mozilla::gmp::CDMKeyInformation>&)
Unexecuted instantiation: void mozilla::gmp::ChromiumCDMChild::CallOnMessageLoopThread<bool (mozilla::gmp::PChromiumCDMChild::*)(nsTString<char> const&, double const&), nsTString<char>, double&>(char const*, bool (mozilla::gmp::PChromiumCDMChild::*)(nsTString<char> const&, double const&), nsTString<char>&&, double&)
Unexecuted instantiation: void mozilla::gmp::ChromiumCDMChild::CallOnMessageLoopThread<bool (mozilla::gmp::PChromiumCDMChild::*)(nsTString<char> const&), nsTString<char> >(char const*, bool (mozilla::gmp::PChromiumCDMChild::*)(nsTString<char> const&), nsTString<char>&&)
227
228
void
229
ChromiumCDMChild::OnResolveKeyStatusPromise(uint32_t aPromiseId,
230
0
                                            cdm::KeyStatus aKeyStatus) {
231
0
  GMP_LOG("ChromiumCDMChild::OnResolveKeyStatusPromise(pid=%" PRIu32 "keystatus=%d)",
232
0
          aPromiseId,
233
0
          aKeyStatus);
234
0
  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnResolveKeyStatusPromise",
235
0
                          &ChromiumCDMChild::SendOnResolvePromiseWithKeyStatus,
236
0
                          aPromiseId,
237
0
                          static_cast<uint32_t>(aKeyStatus));
238
0
}
239
240
bool
241
ChromiumCDMChild::OnResolveNewSessionPromiseInternal(uint32_t aPromiseId,
242
                                                     const nsCString& aSessionId)
243
0
{
244
0
  MOZ_ASSERT(IsOnMessageLoopThread());
245
0
  if (mLoadSessionPromiseIds.Contains(aPromiseId)) {
246
0
    // As laid out in the Chromium CDM API, if the CDM fails to load
247
0
    // a session it calls OnResolveNewSessionPromise with nullptr as the sessionId.
248
0
    // We can safely assume this means that we have failed to load a session
249
0
    // as the other methods specify calling 'OnRejectPromise' when they fail.
250
0
    bool loadSuccessful = !aSessionId.IsEmpty();
251
0
    GMP_LOG("ChromiumCDMChild::OnResolveNewSessionPromise(pid=%u, sid=%s) "
252
0
            "resolving %s load session ",
253
0
            aPromiseId,
254
0
            aSessionId.get(),
255
0
            (loadSuccessful ? "successful" : "failed"));
256
0
    mLoadSessionPromiseIds.RemoveElement(aPromiseId);
257
0
    return SendResolveLoadSessionPromise(aPromiseId, loadSuccessful);
258
0
  }
259
0
260
0
  return SendOnResolveNewSessionPromise(aPromiseId,
261
0
                                        aSessionId);
262
0
}
263
void
264
ChromiumCDMChild::OnResolveNewSessionPromise(uint32_t aPromiseId,
265
                                             const char* aSessionId,
266
                                             uint32_t aSessionIdSize)
267
0
{
268
0
  GMP_LOG("ChromiumCDMChild::OnResolveNewSessionPromise(pid=%" PRIu32
269
0
          ", sid=%s)",
270
0
          aPromiseId,
271
0
          aSessionId);
272
0
  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnResolveNewSessionPromise",
273
0
                          &ChromiumCDMChild::OnResolveNewSessionPromiseInternal,
274
0
                          aPromiseId,
275
0
                          nsCString(aSessionId, aSessionIdSize));
276
0
277
0
}
278
279
void ChromiumCDMChild::OnResolvePromise(uint32_t aPromiseId)
280
0
{
281
0
  GMP_LOG("ChromiumCDMChild::OnResolvePromise(pid=%" PRIu32 ")", aPromiseId);
282
0
  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnResolvePromise",
283
0
                          &ChromiumCDMChild::SendOnResolvePromise,
284
0
                          aPromiseId);
285
0
}
286
287
void
288
ChromiumCDMChild::OnRejectPromise(uint32_t aPromiseId,
289
                                  cdm::Exception aException,
290
                                  uint32_t aSystemCode,
291
                                  const char* aErrorMessage,
292
                                  uint32_t aErrorMessageSize)
293
0
{
294
0
  GMP_LOG("ChromiumCDMChild::OnRejectPromise(pid=%" PRIu32 ", err=%" PRIu32
295
0
          " code=%" PRIu32 ", msg='%s')",
296
0
          aPromiseId,
297
0
          aException,
298
0
          aSystemCode,
299
0
          aErrorMessage);
300
0
  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnRejectPromise",
301
0
                          &ChromiumCDMChild::SendOnRejectPromise,
302
0
                          aPromiseId,
303
0
                          static_cast<uint32_t>(aException),
304
0
                          aSystemCode,
305
0
                          nsCString(aErrorMessage, aErrorMessageSize));
306
0
}
307
308
void
309
ChromiumCDMChild::OnSessionMessage(const char* aSessionId,
310
                                   uint32_t aSessionIdSize,
311
                                   cdm::MessageType aMessageType,
312
                                   const char* aMessage,
313
                                   uint32_t aMessageSize)
314
0
{
315
0
  GMP_LOG("ChromiumCDMChild::OnSessionMessage(sid=%s, type=%" PRIu32
316
0
          " size=%" PRIu32 ")",
317
0
          aSessionId,
318
0
          aMessageType,
319
0
          aMessageSize);
320
0
  nsTArray<uint8_t> message;
321
0
  message.AppendElements(aMessage, aMessageSize);
322
0
  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnSessionMessage",
323
0
                          &ChromiumCDMChild::SendOnSessionMessage,
324
0
                          nsCString(aSessionId, aSessionIdSize),
325
0
                          static_cast<uint32_t>(aMessageType),
326
0
                          message);
327
0
}
328
329
static nsCString
330
ToString(const cdm::KeyInformation* aKeysInfo, uint32_t aKeysInfoCount)
331
0
{
332
0
  nsCString str;
333
0
  for (uint32_t i = 0; i < aKeysInfoCount; i++) {
334
0
    if (!str.IsEmpty()) {
335
0
      str.AppendLiteral(",");
336
0
    }
337
0
    const cdm::KeyInformation& key = aKeysInfo[i];
338
0
    str.Append(ToHexString(key.key_id, key.key_id_size));
339
0
    str.AppendLiteral("=");
340
0
    str.AppendInt(key.status);
341
0
  }
342
0
  return str;
343
0
}
344
345
void
346
ChromiumCDMChild::OnSessionKeysChange(const char *aSessionId,
347
                                      uint32_t aSessionIdSize,
348
                                      bool aHasAdditionalUsableKey,
349
                                      const cdm::KeyInformation* aKeysInfo,
350
                                      uint32_t aKeysInfoCount)
351
0
{
352
0
  GMP_LOG("ChromiumCDMChild::OnSessionKeysChange(sid=%s) keys={%s}",
353
0
          aSessionId,
354
0
          ToString(aKeysInfo, aKeysInfoCount).get());
355
0
356
0
  nsTArray<CDMKeyInformation> keys;
357
0
  keys.SetCapacity(aKeysInfoCount);
358
0
  for (uint32_t i = 0; i < aKeysInfoCount; i++) {
359
0
    const cdm::KeyInformation& key = aKeysInfo[i];
360
0
    nsTArray<uint8_t> kid;
361
0
    kid.AppendElements(key.key_id, key.key_id_size);
362
0
    keys.AppendElement(CDMKeyInformation(kid, key.status, key.system_code));
363
0
  }
364
0
  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnSessionMessage",
365
0
                          &ChromiumCDMChild::SendOnSessionKeysChange,
366
0
                          nsCString(aSessionId, aSessionIdSize),
367
0
                          keys);
368
0
369
0
}
370
371
void
372
ChromiumCDMChild::OnExpirationChange(const char* aSessionId,
373
                                     uint32_t aSessionIdSize,
374
                                     cdm::Time aNewExpiryTime)
375
0
{
376
0
  GMP_LOG("ChromiumCDMChild::OnExpirationChange(sid=%s, time=%lf)",
377
0
          aSessionId,
378
0
          aNewExpiryTime);
379
0
  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnExpirationChange",
380
0
                          &ChromiumCDMChild::SendOnExpirationChange,
381
0
                          nsCString(aSessionId, aSessionIdSize),
382
0
                          aNewExpiryTime);
383
0
}
384
385
void
386
ChromiumCDMChild::OnSessionClosed(const char* aSessionId,
387
                                  uint32_t aSessionIdSize)
388
0
{
389
0
  GMP_LOG("ChromiumCDMChild::OnSessionClosed(sid=%s)", aSessionId);
390
0
  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnSessionClosed",
391
0
                          &ChromiumCDMChild::SendOnSessionClosed,
392
0
                          nsCString(aSessionId, aSessionIdSize));
393
0
}
394
395
cdm::FileIO*
396
ChromiumCDMChild::CreateFileIO(cdm::FileIOClient * aClient)
397
0
{
398
0
  MOZ_ASSERT(IsOnMessageLoopThread());
399
0
  GMP_LOG("ChromiumCDMChild::CreateFileIO()");
400
0
  if (!mPersistentStateAllowed) {
401
0
    return nullptr;
402
0
  }
403
0
  return new WidevineFileIO(aClient);
404
0
}
405
406
void
407
ChromiumCDMChild::RequestStorageId(uint32_t aVersion)
408
0
{
409
0
  MOZ_ASSERT(IsOnMessageLoopThread());
410
0
  GMP_LOG("ChromiumCDMChild::RequestStorageId() aVersion = %u", aVersion);
411
0
  // aVersion >= 0x80000000 are reserved.
412
0
  if (aVersion >= 0x80000000) {
413
0
    mCDM->OnStorageId(aVersion, nullptr, 0);
414
0
    return;
415
0
  }
416
0
  if (aVersion > CDMStorageIdProvider::kCurrentVersion) {
417
0
    mCDM->OnStorageId(aVersion, nullptr, 0);
418
0
    return;
419
0
  }
420
0
421
0
  mCDM->OnStorageId(CDMStorageIdProvider::kCurrentVersion,
422
0
                    !mStorageId.IsEmpty()
423
0
                      ? reinterpret_cast<const uint8_t*>(mStorageId.get())
424
0
                      : nullptr,
425
0
                    mStorageId.Length());
426
0
}
427
428
ChromiumCDMChild::~ChromiumCDMChild()
429
0
{
430
0
  GMP_LOG("ChromiumCDMChild:: dtor this=%p", this);
431
0
}
432
433
bool
434
ChromiumCDMChild::IsOnMessageLoopThread()
435
0
{
436
0
  return mPlugin && mPlugin->GMPMessageLoop() == MessageLoop::current();
437
0
}
438
439
void
440
ChromiumCDMChild::PurgeShmems()
441
0
{
442
0
  for (ipc::Shmem& shmem : mBuffers) {
443
0
    DeallocShmem(shmem);
444
0
  }
445
0
  mBuffers.Clear();
446
0
}
447
448
ipc::IPCResult
449
ChromiumCDMChild::RecvPurgeShmems()
450
0
{
451
0
  PurgeShmems();
452
0
  return IPC_OK();
453
0
}
454
455
mozilla::ipc::IPCResult
456
ChromiumCDMChild::RecvInit(const bool& aAllowDistinctiveIdentifier,
457
                           const bool& aAllowPersistentState)
458
0
{
459
0
  MOZ_ASSERT(IsOnMessageLoopThread());
460
0
  GMP_LOG("ChromiumCDMChild::RecvInit(distinctiveId=%s, persistentState=%s)",
461
0
          aAllowDistinctiveIdentifier ? "true" : "false",
462
0
          aAllowPersistentState ? "true" : "false");
463
0
  mPersistentStateAllowed = aAllowPersistentState;
464
0
  if (mCDM) {
465
0
    mCDM->Initialize(aAllowDistinctiveIdentifier,
466
0
                     aAllowPersistentState,
467
0
                     // We do not yet support hardware secure codecs
468
0
                     false);
469
0
  }
470
0
  return IPC_OK();
471
0
}
472
473
mozilla::ipc::IPCResult
474
ChromiumCDMChild::RecvSetServerCertificate(const uint32_t& aPromiseId,
475
                                           nsTArray<uint8_t>&& aServerCert)
476
477
0
{
478
0
  MOZ_ASSERT(IsOnMessageLoopThread());
479
0
  GMP_LOG("ChromiumCDMChild::RecvSetServerCertificate() certlen=%zu",
480
0
          aServerCert.Length());
481
0
  if (mCDM) {
482
0
    mCDM->SetServerCertificate(aPromiseId,
483
0
                               aServerCert.Elements(),
484
0
                               aServerCert.Length());
485
0
  }
486
0
  return IPC_OK();
487
0
}
488
489
mozilla::ipc::IPCResult
490
ChromiumCDMChild::RecvCreateSessionAndGenerateRequest(
491
  const uint32_t& aPromiseId,
492
  const uint32_t& aSessionType,
493
  const uint32_t& aInitDataType,
494
  nsTArray<uint8_t>&& aInitData)
495
0
{
496
0
  MOZ_ASSERT(IsOnMessageLoopThread());
497
0
  GMP_LOG("ChromiumCDMChild::RecvCreateSessionAndGenerateRequest("
498
0
          "pid=%" PRIu32 ", sessionType=%" PRIu32 ", initDataType=%" PRIu32
499
0
          ") initDataLen=%zu",
500
0
          aPromiseId,
501
0
          aSessionType,
502
0
          aInitDataType,
503
0
          aInitData.Length());
504
0
  MOZ_ASSERT(aSessionType <= cdm::SessionType::kPersistentKeyRelease);
505
0
  MOZ_ASSERT(aInitDataType <= cdm::InitDataType::kWebM);
506
0
  if (mCDM) {
507
0
    mCDM->CreateSessionAndGenerateRequest(aPromiseId,
508
0
                                          static_cast<cdm::SessionType>(aSessionType),
509
0
                                          static_cast<cdm::InitDataType>(aInitDataType),
510
0
                                          aInitData.Elements(),
511
0
                                          aInitData.Length());
512
0
  }
513
0
  return IPC_OK();
514
0
}
515
516
mozilla::ipc::IPCResult
517
ChromiumCDMChild::RecvLoadSession(const uint32_t& aPromiseId,
518
                                  const uint32_t& aSessionType,
519
                                  const nsCString& aSessionId)
520
0
{
521
0
  MOZ_ASSERT(IsOnMessageLoopThread());
522
0
  GMP_LOG("ChromiumCDMChild::RecvLoadSession(pid=%u, type=%u, sessionId=%s)",
523
0
          aPromiseId,
524
0
          aSessionType,
525
0
          aSessionId.get());
526
0
  if (mCDM) {
527
0
    mLoadSessionPromiseIds.AppendElement(aPromiseId);
528
0
    mCDM->LoadSession(aPromiseId,
529
0
                      static_cast<cdm::SessionType>(aSessionType),
530
0
                      aSessionId.get(),
531
0
                      aSessionId.Length());
532
0
  }
533
0
  return IPC_OK();
534
0
}
535
536
mozilla::ipc::IPCResult
537
ChromiumCDMChild::RecvUpdateSession(const uint32_t& aPromiseId,
538
                                    const nsCString& aSessionId,
539
                                    nsTArray<uint8_t>&& aResponse)
540
0
{
541
0
  MOZ_ASSERT(IsOnMessageLoopThread());
542
0
  GMP_LOG("ChromiumCDMChild::RecvUpdateSession(pid=%" PRIu32
543
0
          ", sid=%s) responseLen=%zu",
544
0
          aPromiseId,
545
0
          aSessionId.get(),
546
0
          aResponse.Length());
547
0
  if (mCDM) {
548
0
    mCDM->UpdateSession(aPromiseId,
549
0
                        aSessionId.get(),
550
0
                        aSessionId.Length(),
551
0
                        aResponse.Elements(),
552
0
                        aResponse.Length());
553
0
  }
554
0
  return IPC_OK();
555
0
}
556
557
mozilla::ipc::IPCResult
558
ChromiumCDMChild::RecvCloseSession(const uint32_t& aPromiseId,
559
                                   const nsCString& aSessionId)
560
0
{
561
0
  MOZ_ASSERT(IsOnMessageLoopThread());
562
0
  GMP_LOG("ChromiumCDMChild::RecvCloseSession(pid=%" PRIu32 ", sid=%s)",
563
0
          aPromiseId,
564
0
          aSessionId.get());
565
0
  if (mCDM) {
566
0
    mCDM->CloseSession(aPromiseId, aSessionId.get(), aSessionId.Length());
567
0
  }
568
0
  return IPC_OK();
569
0
}
570
571
mozilla::ipc::IPCResult
572
ChromiumCDMChild::RecvRemoveSession(const uint32_t& aPromiseId,
573
                                    const nsCString& aSessionId)
574
0
{
575
0
  MOZ_ASSERT(IsOnMessageLoopThread());
576
0
  GMP_LOG("ChromiumCDMChild::RecvRemoveSession(pid=%" PRIu32 ", sid=%s)",
577
0
          aPromiseId,
578
0
          aSessionId.get());
579
0
  if (mCDM) {
580
0
    mCDM->RemoveSession(aPromiseId, aSessionId.get(), aSessionId.Length());
581
0
  }
582
0
  return IPC_OK();
583
0
}
584
585
// See https://cs.chromium.org/chromium/src/media/blink/webcontentdecryptionmodule_impl.cc?rcl=9d4e17194fbae2839d269e0b625520eac09efa9b&l=40
586
static cdm::HdcpVersion
587
ToCDMHdcpVersion(const nsCString& aMinHdcpVersion)
588
0
{
589
0
  // String compare with ignoring case.
590
0
  if (aMinHdcpVersion.IsEmpty()) {
591
0
    return cdm::HdcpVersion::kHdcpVersionNone;
592
0
  }
593
0
  if (aMinHdcpVersion.EqualsIgnoreCase("hdcp-1.0")) {
594
0
    return cdm::HdcpVersion::kHdcpVersion1_0;
595
0
  }
596
0
  if (aMinHdcpVersion.EqualsIgnoreCase("hdcp-1.1")) {
597
0
    return cdm::HdcpVersion::kHdcpVersion1_1;
598
0
  }
599
0
  if (aMinHdcpVersion.EqualsIgnoreCase("hdcp-1.2")) {
600
0
    return cdm::HdcpVersion::kHdcpVersion1_2;
601
0
  }
602
0
  if (aMinHdcpVersion.EqualsIgnoreCase("hdcp-1.3")) {
603
0
    return cdm::HdcpVersion::kHdcpVersion1_3;
604
0
  }
605
0
  if (aMinHdcpVersion.EqualsIgnoreCase("hdcp-1.4")) {
606
0
    return cdm::HdcpVersion::kHdcpVersion1_4;
607
0
  }
608
0
  if (aMinHdcpVersion.EqualsIgnoreCase("hdcp-2.0")) {
609
0
    return cdm::HdcpVersion::kHdcpVersion2_0;
610
0
  }
611
0
  if (aMinHdcpVersion.EqualsIgnoreCase("hdcp-2.1")) {
612
0
    return cdm::HdcpVersion::kHdcpVersion2_1;
613
0
  }
614
0
  if (aMinHdcpVersion.EqualsIgnoreCase("hdcp-2.2")) {
615
0
    return cdm::HdcpVersion::kHdcpVersion2_2;
616
0
  }
617
0
618
0
  // Invalid hdcp version string.
619
0
  return cdm::HdcpVersion::kHdcpVersionNone;
620
0
}
621
622
mozilla::ipc::IPCResult
623
ChromiumCDMChild::RecvGetStatusForPolicy(const uint32_t& aPromiseId,
624
                                         const nsCString& aMinHdcpVersion)
625
0
{
626
0
  MOZ_ASSERT(IsOnMessageLoopThread());
627
0
  GMP_LOG("ChromiumCDMChild::RecvGetStatusForPolicy(pid=%" PRIu32 ", MinHdcpVersion=%s)",
628
0
          aPromiseId,
629
0
          aMinHdcpVersion.get());
630
0
  if (mCDM) {
631
0
    cdm::Policy policy;
632
0
    // We didn't check the return value of ToCDMHdcpVersion.
633
0
    // Let CDM to handle the cdm::HdcpVersion::kHdcpVersionNone case.
634
0
    // CDM will callback by OnResolveKeyStatusPromise when it successfully executes.
635
0
    policy.min_hdcp_version = ToCDMHdcpVersion(aMinHdcpVersion);
636
0
    mCDM->GetStatusForPolicy(aPromiseId, policy);
637
0
  }
638
0
  return IPC_OK();
639
0
}
640
641
static cdm::EncryptionScheme
642
ConvertToCdmEncryptionScheme(const GMPEncryptionScheme& aEncryptionScheme)
643
0
{
644
0
  switch (aEncryptionScheme) {
645
0
    case GMPEncryptionScheme::kGMPEncryptionNone:
646
0
      return cdm::EncryptionScheme::kUnencrypted;
647
0
    case GMPEncryptionScheme::kGMPEncryptionCenc:
648
0
      return cdm::EncryptionScheme::kCenc;
649
0
    case GMPEncryptionScheme::kGMPEncryptionCbcs:
650
0
      return cdm::EncryptionScheme::kCbcs;
651
0
    default:
652
0
      MOZ_ASSERT_UNREACHABLE("Cannot convert invalid encryption scheme!");
653
0
      return cdm::EncryptionScheme::kUnencrypted;
654
0
  }
655
0
}
656
657
static void
658
InitInputBuffer(const CDMInputBuffer& aBuffer,
659
                nsTArray<cdm::SubsampleEntry>& aSubSamples,
660
                cdm::InputBuffer_2& aInputBuffer)
661
0
{
662
0
  aInputBuffer.data = aBuffer.mData().get<uint8_t>();
663
0
  aInputBuffer.data_size = aBuffer.mData().Size<uint8_t>();
664
0
665
0
  if (aBuffer.mEncryptionScheme() > GMPEncryptionScheme::kGMPEncryptionNone) {
666
0
    // Cbcs is not yet supported, so we expect only cenc if the buffer us
667
0
    // encrypted
668
0
    MOZ_ASSERT(aBuffer.mEncryptionScheme() ==
669
0
               GMPEncryptionScheme::kGMPEncryptionCenc);
670
0
    aInputBuffer.key_id = aBuffer.mKeyId().Elements();
671
0
    aInputBuffer.key_id_size = aBuffer.mKeyId().Length();
672
0
673
0
    aInputBuffer.iv = aBuffer.mIV().Elements();
674
0
    aInputBuffer.iv_size = aBuffer.mIV().Length();
675
0
676
0
    aSubSamples.SetCapacity(aBuffer.mClearBytes().Length());
677
0
    for (size_t i = 0; i < aBuffer.mCipherBytes().Length(); i++) {
678
0
      aSubSamples.AppendElement(cdm::SubsampleEntry{
679
0
        aBuffer.mClearBytes()[i], aBuffer.mCipherBytes()[i] });
680
0
    }
681
0
    aInputBuffer.subsamples = aSubSamples.Elements();
682
0
    aInputBuffer.num_subsamples = aSubSamples.Length();
683
0
    aInputBuffer.encryption_scheme =
684
0
      ConvertToCdmEncryptionScheme(aBuffer.mEncryptionScheme());
685
0
  }
686
0
  aInputBuffer.timestamp = aBuffer.mTimestamp();
687
0
}
688
689
bool
690
ChromiumCDMChild::HasShmemOfSize(size_t aSize) const
691
0
{
692
0
  for (const ipc::Shmem& shmem : mBuffers) {
693
0
    if (shmem.Size<uint8_t>() == aSize) {
694
0
      return true;
695
0
    }
696
0
  }
697
0
  return false;
698
0
}
699
700
mozilla::ipc::IPCResult
701
ChromiumCDMChild::RecvDecrypt(const uint32_t& aId,
702
                              const CDMInputBuffer& aBuffer)
703
0
{
704
0
  MOZ_ASSERT(IsOnMessageLoopThread());
705
0
  GMP_LOG("ChromiumCDMChild::RecvDecrypt()");
706
0
707
0
  // Parent should have already gifted us a shmem to use as output.
708
0
  size_t outputShmemSize = aBuffer.mData().Size<uint8_t>();
709
0
  MOZ_ASSERT(HasShmemOfSize(outputShmemSize));
710
0
711
0
  // Ensure we deallocate the shmem used to send input.
712
0
  RefPtr<ChromiumCDMChild> self = this;
713
0
  auto autoDeallocateInputShmem =
714
0
    MakeScopeExit([&, self] { self->DeallocShmem(aBuffer.mData()); });
715
0
716
0
  // On failure, we need to ensure that the shmem that the parent sent
717
0
  // for the CDM to use to return output back to the parent is deallocated.
718
0
  // Otherwise, it will leak.
719
0
  auto autoDeallocateOutputShmem = MakeScopeExit([self, outputShmemSize] {
720
0
    self->mBuffers.RemoveElementsBy([outputShmemSize, self](ipc::Shmem& aShmem) {
721
0
      if (aShmem.Size<uint8_t>() != outputShmemSize) {
722
0
        return false;
723
0
      }
724
0
      self->DeallocShmem(aShmem);
725
0
      return true;
726
0
    });
727
0
  });
728
0
729
0
  if (!mCDM) {
730
0
    GMP_LOG("ChromiumCDMChild::RecvDecrypt() no CDM");
731
0
    Unused << SendDecryptFailed(aId, cdm::kDecryptError);
732
0
    return IPC_OK();
733
0
  }
734
0
  if (aBuffer.mClearBytes().Length() != aBuffer.mCipherBytes().Length()) {
735
0
    GMP_LOG("ChromiumCDMChild::RecvDecrypt() clear/cipher bytes length doesn't "
736
0
            "match");
737
0
    Unused << SendDecryptFailed(aId, cdm::kDecryptError);
738
0
    return IPC_OK();
739
0
  }
740
0
741
0
  cdm::InputBuffer_2 input = {};
742
0
  nsTArray<cdm::SubsampleEntry> subsamples;
743
0
  InitInputBuffer(aBuffer, subsamples, input);
744
0
745
0
  WidevineDecryptedBlock output;
746
0
  cdm::Status status = mCDM->Decrypt(input, &output);
747
0
748
0
  // CDM should have allocated a cdm::Buffer for output.
749
0
  CDMShmemBuffer* buffer =
750
0
    output.DecryptedBuffer()
751
0
      ? static_cast<CDMShmemBuffer*>(output.DecryptedBuffer())
752
0
      : nullptr;
753
0
  MOZ_ASSERT_IF(buffer, buffer->AsShmemBuffer());
754
0
  if (status != cdm::kSuccess || !buffer) {
755
0
    Unused << SendDecryptFailed(aId, status);
756
0
    return IPC_OK();
757
0
  }
758
0
759
0
  // Success! Return the decrypted sample to parent.
760
0
  MOZ_ASSERT(!HasShmemOfSize(outputShmemSize));
761
0
  ipc::Shmem shmem = buffer->ExtractShmem();
762
0
  if (SendDecrypted(aId, cdm::kSuccess, shmem)) {
763
0
    // No need to deallocate the output shmem; it should have been returned
764
0
    // to the content process.
765
0
    autoDeallocateOutputShmem.release();
766
0
  }
767
0
768
0
  return IPC_OK();
769
0
}
770
771
mozilla::ipc::IPCResult
772
ChromiumCDMChild::RecvInitializeVideoDecoder(
773
  const CDMVideoDecoderConfig& aConfig)
774
0
{
775
0
  MOZ_ASSERT(IsOnMessageLoopThread());
776
0
  MOZ_ASSERT(!mDecoderInitialized);
777
0
  if (!mCDM) {
778
0
    GMP_LOG("ChromiumCDMChild::RecvInitializeVideoDecoder() no CDM");
779
0
    Unused << SendOnDecoderInitDone(cdm::kInitializationError);
780
0
    return IPC_OK();
781
0
  }
782
0
  cdm::VideoDecoderConfig_2 config = {};
783
0
  config.codec = static_cast<cdm::VideoCodec>(aConfig.mCodec());
784
0
  config.profile = static_cast<cdm::VideoCodecProfile>(aConfig.mProfile());
785
0
  config.format = static_cast<cdm::VideoFormat>(aConfig.mFormat());
786
0
  config.coded_size =
787
0
    mCodedSize = { aConfig.mImageWidth(), aConfig.mImageHeight() };
788
0
  nsTArray<uint8_t> extraData(aConfig.mExtraData());
789
0
  config.extra_data = extraData.Elements();
790
0
  config.extra_data_size = extraData.Length();
791
0
  config.encryption_scheme =
792
0
    ConvertToCdmEncryptionScheme(aConfig.mEncryptionScheme());
793
0
  cdm::Status status = mCDM->InitializeVideoDecoder(config);
794
0
  GMP_LOG("ChromiumCDMChild::RecvInitializeVideoDecoder() status=%u", status);
795
0
  Unused << SendOnDecoderInitDone(status);
796
0
  mDecoderInitialized = status == cdm::kSuccess;
797
0
  return IPC_OK();
798
0
}
799
800
mozilla::ipc::IPCResult
801
ChromiumCDMChild::RecvDeinitializeVideoDecoder()
802
0
{
803
0
  MOZ_ASSERT(IsOnMessageLoopThread());
804
0
  GMP_LOG("ChromiumCDMChild::RecvDeinitializeVideoDecoder()");
805
0
  MOZ_ASSERT(mDecoderInitialized);
806
0
  if (mDecoderInitialized && mCDM) {
807
0
    mCDM->DeinitializeDecoder(cdm::kStreamTypeVideo);
808
0
  }
809
0
  mDecoderInitialized = false;
810
0
  PurgeShmems();
811
0
  return IPC_OK();
812
0
}
813
814
mozilla::ipc::IPCResult
815
ChromiumCDMChild::RecvResetVideoDecoder()
816
0
{
817
0
  MOZ_ASSERT(IsOnMessageLoopThread());
818
0
  GMP_LOG("ChromiumCDMChild::RecvResetVideoDecoder()");
819
0
  if (mDecoderInitialized && mCDM) {
820
0
    mCDM->ResetDecoder(cdm::kStreamTypeVideo);
821
0
  }
822
0
  Unused << SendResetVideoDecoderComplete();
823
0
  return IPC_OK();
824
0
}
825
826
mozilla::ipc::IPCResult
827
ChromiumCDMChild::RecvDecryptAndDecodeFrame(const CDMInputBuffer& aBuffer)
828
0
{
829
0
  MOZ_ASSERT(IsOnMessageLoopThread());
830
0
  GMP_LOG("ChromiumCDMChild::RecvDecryptAndDecodeFrame() t=%" PRId64 ")",
831
0
          aBuffer.mTimestamp());
832
0
  MOZ_ASSERT(mDecoderInitialized);
833
0
834
0
  if (!mCDM) {
835
0
    GMP_LOG("ChromiumCDMChild::RecvDecryptAndDecodeFrame() no CDM");
836
0
    Unused << SendDecodeFailed(cdm::kDecodeError);
837
0
    return IPC_OK();
838
0
  }
839
0
840
0
  RefPtr<ChromiumCDMChild> self = this;
841
0
  auto autoDeallocateShmem = MakeScopeExit([&, self] {
842
0
    self->DeallocShmem(aBuffer.mData());
843
0
  });
844
0
845
0
  // The output frame may not have the same timestamp as the frame we put in.
846
0
  // We may need to input a number of frames before we receive output. The
847
0
  // CDM's decoder reorders to ensure frames output are in presentation order.
848
0
  // So we need to store the durations of the frames input, and retrieve them
849
0
  // on output.
850
0
  mFrameDurations.Insert(aBuffer.mTimestamp(), aBuffer.mDuration());
851
0
852
0
  cdm::InputBuffer_2 input = {};
853
0
  nsTArray<cdm::SubsampleEntry> subsamples;
854
0
  InitInputBuffer(aBuffer, subsamples, input);
855
0
856
0
  WidevineVideoFrame frame;
857
0
  cdm::Status rv = mCDM->DecryptAndDecodeFrame(input, &frame);
858
0
  GMP_LOG("ChromiumCDMChild::RecvDecryptAndDecodeFrame() t=%" PRId64
859
0
          " CDM decoder rv=%d",
860
0
          aBuffer.mTimestamp(),
861
0
          rv);
862
0
863
0
  switch (rv) {
864
0
    case cdm::kNeedMoreData:
865
0
      Unused << SendDecodeFailed(rv);
866
0
      break;
867
0
    case cdm::kNoKey:
868
0
      GMP_LOG("NoKey for sample at time=%" PRId64 "!", input.timestamp);
869
0
      // Somehow our key became unusable. Typically this would happen when
870
0
      // a stream requires output protection, and the configuration changed
871
0
      // such that output protection is no longer available. For example, a
872
0
      // non-compliant monitor was attached. The JS player should notice the
873
0
      // key status changing to "output-restricted", and is supposed to switch
874
0
      // to a stream that doesn't require OP. In order to keep the playback
875
0
      // pipeline rolling, just output a black frame. See bug 1343140.
876
0
      if (!frame.InitToBlack(mCodedSize.width, mCodedSize.height,
877
0
                             input.timestamp)) {
878
0
        Unused << SendDecodeFailed(cdm::kDecodeError);
879
0
        break;
880
0
      }
881
0
      MOZ_FALLTHROUGH;
882
0
    case cdm::kSuccess:
883
0
      if (frame.FrameBuffer()) {
884
0
        ReturnOutput(frame);
885
0
        break;
886
0
      }
887
0
      // CDM didn't set a frame buffer on the sample, report it as an error.
888
0
      MOZ_FALLTHROUGH;
889
0
    default:
890
0
      Unused << SendDecodeFailed(rv);
891
0
      break;
892
0
  }
893
0
894
0
  return IPC_OK();
895
0
}
896
897
void
898
ChromiumCDMChild::ReturnOutput(WidevineVideoFrame& aFrame)
899
0
{
900
0
  MOZ_ASSERT(IsOnMessageLoopThread());
901
0
  MOZ_ASSERT(aFrame.FrameBuffer());
902
0
  gmp::CDMVideoFrame output;
903
0
  output.mFormat() = static_cast<cdm::VideoFormat>(aFrame.Format());
904
0
  output.mImageWidth() = aFrame.Size().width;
905
0
  output.mImageHeight() = aFrame.Size().height;
906
0
  output.mYPlane() = { aFrame.PlaneOffset(cdm::VideoFrame::kYPlane),
907
0
                       aFrame.Stride(cdm::VideoFrame::kYPlane) };
908
0
  output.mUPlane() = { aFrame.PlaneOffset(cdm::VideoFrame::kUPlane),
909
0
                       aFrame.Stride(cdm::VideoFrame::kUPlane) };
910
0
  output.mVPlane() = { aFrame.PlaneOffset(cdm::VideoFrame::kVPlane),
911
0
                       aFrame.Stride(cdm::VideoFrame::kVPlane) };
912
0
  output.mTimestamp() = aFrame.Timestamp();
913
0
914
0
  uint64_t duration = 0;
915
0
  if (mFrameDurations.Find(aFrame.Timestamp(), duration)) {
916
0
    output.mDuration() = duration;
917
0
  }
918
0
919
0
  CDMBuffer* base = reinterpret_cast<CDMBuffer*>(aFrame.FrameBuffer());
920
0
  if (base->AsShmemBuffer()) {
921
0
    ipc::Shmem shmem = base->AsShmemBuffer()->ExtractShmem();
922
0
    Unused << SendDecodedShmem(output, shmem);
923
0
  } else {
924
0
    MOZ_ASSERT(base->AsArrayBuffer());
925
0
    Unused << SendDecodedData(output, base->AsArrayBuffer()->ExtractBuffer());
926
0
  }
927
0
}
928
929
mozilla::ipc::IPCResult
930
ChromiumCDMChild::RecvDrain()
931
0
{
932
0
  MOZ_ASSERT(IsOnMessageLoopThread());
933
0
  if (!mCDM) {
934
0
    GMP_LOG("ChromiumCDMChild::RecvDrain() no CDM");
935
0
    Unused << SendDrainComplete();
936
0
    return IPC_OK();
937
0
  }
938
0
  WidevineVideoFrame frame;
939
0
  cdm::InputBuffer_2 sample = {};
940
0
  cdm::Status rv = mCDM->DecryptAndDecodeFrame(sample, &frame);
941
0
  GMP_LOG("ChromiumCDMChild::RecvDrain();  DecryptAndDecodeFrame() rv=%d", rv);
942
0
  if (rv == cdm::kSuccess) {
943
0
    MOZ_ASSERT(frame.Format() != cdm::kUnknownVideoFormat);
944
0
    ReturnOutput(frame);
945
0
  } else {
946
0
    Unused << SendDrainComplete();
947
0
  }
948
0
  return IPC_OK();
949
0
}
950
951
mozilla::ipc::IPCResult
952
ChromiumCDMChild::RecvDestroy()
953
0
{
954
0
  MOZ_ASSERT(IsOnMessageLoopThread());
955
0
  GMP_LOG("ChromiumCDMChild::RecvDestroy()");
956
0
957
0
  MOZ_ASSERT(!mDecoderInitialized);
958
0
959
0
  if (mCDM) {
960
0
    mCDM->Destroy();
961
0
    mCDM = nullptr;
962
0
  }
963
0
  mDestroyed = true;
964
0
965
0
  Unused << Send__delete__(this);
966
0
967
0
  return IPC_OK();
968
0
}
969
970
mozilla::ipc::IPCResult
971
ChromiumCDMChild::RecvGiveBuffer(ipc::Shmem&& aBuffer)
972
0
{
973
0
  MOZ_ASSERT(IsOnMessageLoopThread());
974
0
975
0
  GiveBuffer(std::move(aBuffer));
976
0
  return IPC_OK();
977
0
}
978
979
void
980
ChromiumCDMChild::GiveBuffer(ipc::Shmem&& aBuffer)
981
0
{
982
0
  MOZ_ASSERT(IsOnMessageLoopThread());
983
0
  size_t sz = aBuffer.Size<uint8_t>();
984
0
  mBuffers.AppendElement(std::move(aBuffer));
985
0
  GMP_LOG("ChromiumCDMChild::RecvGiveBuffer(capacity=%zu"
986
0
          ") bufferSizes={%s} mDecoderInitialized=%d",
987
0
          sz,
988
0
          ToString(mBuffers).get(),
989
0
          mDecoderInitialized);
990
0
}
991
992
} // namespace gmp
993
} // namespace mozilla