/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 |