/src/mozilla-central/dom/media/gmp/GMPContentChild.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 "GMPContentChild.h" |
7 | | #include "GMPChild.h" |
8 | | #include "GMPVideoDecoderChild.h" |
9 | | #include "GMPVideoEncoderChild.h" |
10 | | #include "ChromiumCDMChild.h" |
11 | | #include "base/task.h" |
12 | | #include "GMPUtils.h" |
13 | | |
14 | | namespace mozilla { |
15 | | namespace gmp { |
16 | | |
17 | | GMPContentChild::GMPContentChild(GMPChild* aChild) |
18 | | : mGMPChild(aChild) |
19 | 0 | { |
20 | 0 | MOZ_COUNT_CTOR(GMPContentChild); |
21 | 0 | } |
22 | | |
23 | | GMPContentChild::~GMPContentChild() |
24 | 0 | { |
25 | 0 | MOZ_COUNT_DTOR(GMPContentChild); |
26 | 0 | } |
27 | | |
28 | | MessageLoop* |
29 | | GMPContentChild::GMPMessageLoop() |
30 | 0 | { |
31 | 0 | return mGMPChild->GMPMessageLoop(); |
32 | 0 | } |
33 | | |
34 | | void |
35 | | GMPContentChild::CheckThread() |
36 | 0 | { |
37 | 0 | MOZ_ASSERT(mGMPChild->mGMPMessageLoop == MessageLoop::current()); |
38 | 0 | } |
39 | | |
40 | | void |
41 | | GMPContentChild::ActorDestroy(ActorDestroyReason aWhy) |
42 | 0 | { |
43 | 0 | mGMPChild->GMPContentChildActorDestroy(this); |
44 | 0 | } |
45 | | |
46 | | void |
47 | | GMPContentChild::ProcessingError(Result aCode, const char* aReason) |
48 | 0 | { |
49 | 0 | mGMPChild->ProcessingError(aCode, aReason); |
50 | 0 | } |
51 | | |
52 | | PGMPVideoDecoderChild* |
53 | | GMPContentChild::AllocPGMPVideoDecoderChild(const uint32_t& aDecryptorId) |
54 | 0 | { |
55 | 0 | GMPVideoDecoderChild* actor = new GMPVideoDecoderChild(this); |
56 | 0 | actor->AddRef(); |
57 | 0 | return actor; |
58 | 0 | } |
59 | | |
60 | | bool |
61 | | GMPContentChild::DeallocPGMPVideoDecoderChild(PGMPVideoDecoderChild* aActor) |
62 | 0 | { |
63 | 0 | static_cast<GMPVideoDecoderChild*>(aActor)->Release(); |
64 | 0 | return true; |
65 | 0 | } |
66 | | |
67 | | PGMPVideoEncoderChild* |
68 | | GMPContentChild::AllocPGMPVideoEncoderChild() |
69 | 0 | { |
70 | 0 | GMPVideoEncoderChild* actor = new GMPVideoEncoderChild(this); |
71 | 0 | actor->AddRef(); |
72 | 0 | return actor; |
73 | 0 | } |
74 | | |
75 | | bool |
76 | | GMPContentChild::DeallocPGMPVideoEncoderChild(PGMPVideoEncoderChild* aActor) |
77 | 0 | { |
78 | 0 | static_cast<GMPVideoEncoderChild*>(aActor)->Release(); |
79 | 0 | return true; |
80 | 0 | } |
81 | | |
82 | | PChromiumCDMChild* |
83 | | GMPContentChild::AllocPChromiumCDMChild() |
84 | 0 | { |
85 | 0 | ChromiumCDMChild* actor = new ChromiumCDMChild(this); |
86 | 0 | actor->AddRef(); |
87 | 0 | return actor; |
88 | 0 | } |
89 | | |
90 | | bool |
91 | | GMPContentChild::DeallocPChromiumCDMChild(PChromiumCDMChild* aActor) |
92 | 0 | { |
93 | 0 | static_cast<ChromiumCDMChild*>(aActor)->Release(); |
94 | 0 | return true; |
95 | 0 | } |
96 | | |
97 | | mozilla::ipc::IPCResult |
98 | | GMPContentChild::RecvPGMPVideoDecoderConstructor(PGMPVideoDecoderChild* aActor, |
99 | | const uint32_t& aDecryptorId) |
100 | 0 | { |
101 | 0 | auto vdc = static_cast<GMPVideoDecoderChild*>(aActor); |
102 | 0 |
|
103 | 0 | void* vd = nullptr; |
104 | 0 | GMPErr err = mGMPChild->GetAPI(GMP_API_VIDEO_DECODER, &vdc->Host(), &vd, aDecryptorId); |
105 | 0 | if (err != GMPNoErr || !vd) { |
106 | 0 | NS_WARNING("GMPGetAPI call failed trying to construct decoder."); |
107 | 0 | return IPC_FAIL_NO_REASON(this); |
108 | 0 | } |
109 | 0 |
|
110 | 0 | vdc->Init(static_cast<GMPVideoDecoder*>(vd)); |
111 | 0 |
|
112 | 0 | return IPC_OK(); |
113 | 0 | } |
114 | | |
115 | | mozilla::ipc::IPCResult |
116 | | GMPContentChild::RecvPGMPVideoEncoderConstructor(PGMPVideoEncoderChild* aActor) |
117 | 0 | { |
118 | 0 | auto vec = static_cast<GMPVideoEncoderChild*>(aActor); |
119 | 0 |
|
120 | 0 | void* ve = nullptr; |
121 | 0 | GMPErr err = mGMPChild->GetAPI(GMP_API_VIDEO_ENCODER, &vec->Host(), &ve); |
122 | 0 | if (err != GMPNoErr || !ve) { |
123 | 0 | NS_WARNING("GMPGetAPI call failed trying to construct encoder."); |
124 | 0 | return IPC_FAIL_NO_REASON(this); |
125 | 0 | } |
126 | 0 |
|
127 | 0 | vec->Init(static_cast<GMPVideoEncoder*>(ve)); |
128 | 0 |
|
129 | 0 | return IPC_OK(); |
130 | 0 | } |
131 | | |
132 | | // Convert CDM10 calls to CDM9 calls, massage args where needed |
133 | | class ChromiumCDM9BackwardsCompat : public cdm::ContentDecryptionModule_10 |
134 | | { |
135 | | public: |
136 | | explicit ChromiumCDM9BackwardsCompat(cdm::Host_10* aHost, |
137 | | cdm::ContentDecryptionModule_9* aCDM) |
138 | | : mCDM(aCDM) |
139 | | , mHost(aHost) |
140 | 0 | { |
141 | 0 | } |
142 | | |
143 | | void Initialize(bool aAllowDistinctiveIdentifier, |
144 | | bool aAllowPersistentState, |
145 | | bool /* aUseHardwareSecureCodec */) override |
146 | 0 | { |
147 | 0 | // aUseHardwareSecureCodec is not used by CDM9 |
148 | 0 | mCDM->Initialize(aAllowDistinctiveIdentifier, aAllowPersistentState); |
149 | 0 | } |
150 | | |
151 | | void GetStatusForPolicy(uint32_t aPromiseId, |
152 | | const cdm::Policy& policy) override |
153 | 0 | { |
154 | 0 | mCDM->GetStatusForPolicy(aPromiseId, policy); |
155 | 0 | } |
156 | | |
157 | | void SetServerCertificate(uint32_t aPromiseId, |
158 | | const uint8_t* aServerCertificateData, |
159 | | uint32_t aServerCertificateDataSize) override |
160 | 0 | { |
161 | 0 | mCDM->SetServerCertificate( |
162 | 0 | aPromiseId, aServerCertificateData, aServerCertificateDataSize); |
163 | 0 | } |
164 | | |
165 | | void CreateSessionAndGenerateRequest(uint32_t aPromiseId, |
166 | | cdm::SessionType aSessionType, |
167 | | cdm::InitDataType aInitDataType, |
168 | | const uint8_t* aInitData, |
169 | | uint32_t aInitDataSize) override |
170 | 0 | { |
171 | 0 | mCDM->CreateSessionAndGenerateRequest( |
172 | 0 | aPromiseId, aSessionType, aInitDataType, aInitData, aInitDataSize); |
173 | 0 | } |
174 | | |
175 | | void LoadSession(uint32_t aPromiseId, |
176 | | cdm::SessionType aSessionType, |
177 | | const char* aSessionId, |
178 | | uint32_t aSessionIdSize) override |
179 | 0 | { |
180 | 0 | mCDM->LoadSession(aPromiseId, aSessionType, aSessionId, aSessionIdSize); |
181 | 0 | } |
182 | | |
183 | | void UpdateSession(uint32_t aPromiseId, |
184 | | const char* aSessionId, |
185 | | uint32_t aSessionIdSize, |
186 | | const uint8_t* aResponse, |
187 | | uint32_t aResponseSize) override |
188 | 0 | { |
189 | 0 | mCDM->UpdateSession( |
190 | 0 | aPromiseId, aSessionId, aSessionIdSize, aResponse, aResponseSize); |
191 | 0 | } |
192 | | |
193 | | void CloseSession(uint32_t aPromiseId, |
194 | | const char* aSessionId, |
195 | | uint32_t aSessionIdSize) override |
196 | 0 | { |
197 | 0 | mCDM->CloseSession(aPromiseId, aSessionId, aSessionIdSize); |
198 | 0 | } |
199 | | |
200 | | void RemoveSession(uint32_t aPromiseId, |
201 | | const char* aSessionId, |
202 | | uint32_t aSessionIdSize) override |
203 | 0 | { |
204 | 0 | mCDM->RemoveSession(aPromiseId, aSessionId, aSessionIdSize); |
205 | 0 | } |
206 | | |
207 | 0 | void TimerExpired(void* aContext) override { mCDM->TimerExpired(aContext); } |
208 | | |
209 | | cdm::Status Decrypt(const cdm::InputBuffer_2& aEncryptedBuffer, |
210 | | cdm::DecryptedBlock* aDecryptedBuffer) override |
211 | 0 | { |
212 | 0 | // Handle possible encryption mismatch |
213 | 0 | if (!IsEncryptionSchemeSupported(aEncryptedBuffer.encryption_scheme)) { |
214 | 0 | return cdm::Status::kDecryptError; |
215 | 0 | } |
216 | 0 | |
217 | 0 | return mCDM->Decrypt(ConvertToInputBuffer_1(aEncryptedBuffer), |
218 | 0 | aDecryptedBuffer); |
219 | 0 | } |
220 | | |
221 | | cdm::Status InitializeAudioDecoder( |
222 | | const cdm::AudioDecoderConfig_2& aAudioDecoderConfig) override |
223 | 0 | { |
224 | 0 | // Handle possible encryption mismatch |
225 | 0 | if (!IsEncryptionSchemeSupported(aAudioDecoderConfig.encryption_scheme)) { |
226 | 0 | return cdm::Status::kInitializationError; |
227 | 0 | } |
228 | 0 | |
229 | 0 | return mCDM->InitializeAudioDecoder( |
230 | 0 | ConverToAudioDecoderConfig_1(aAudioDecoderConfig)); |
231 | 0 | } |
232 | | |
233 | | cdm::Status InitializeVideoDecoder( |
234 | | const cdm::VideoDecoderConfig_2& aVideoDecoderConfig) override |
235 | 0 | { |
236 | 0 | // Handle possible encryption mismatch |
237 | 0 | if (!IsEncryptionSchemeSupported(aVideoDecoderConfig.encryption_scheme)) { |
238 | 0 | return cdm::Status::kInitializationError; |
239 | 0 | } |
240 | 0 | |
241 | 0 | return mCDM->InitializeVideoDecoder( |
242 | 0 | ConvertToVideoDecoderConfig_1(aVideoDecoderConfig)); |
243 | 0 | } |
244 | | |
245 | | void DeinitializeDecoder(cdm::StreamType aDecoderType) override |
246 | 0 | { |
247 | 0 | mCDM->DeinitializeDecoder(aDecoderType); |
248 | 0 | } |
249 | | |
250 | | void ResetDecoder(cdm::StreamType aDecoderType) override |
251 | 0 | { |
252 | 0 | mCDM->ResetDecoder(aDecoderType); |
253 | 0 | } |
254 | | |
255 | | cdm::Status DecryptAndDecodeFrame(const cdm::InputBuffer_2& aEncryptedBuffer, |
256 | | cdm::VideoFrame* aVideoFrame) override |
257 | 0 | { |
258 | 0 | // Handle possible encryption mismatch |
259 | 0 | if (!IsEncryptionSchemeSupported(aEncryptedBuffer.encryption_scheme)) { |
260 | 0 | return cdm::Status::kDecryptError; |
261 | 0 | } |
262 | 0 | |
263 | 0 | return mCDM->DecryptAndDecodeFrame(ConvertToInputBuffer_1(aEncryptedBuffer), |
264 | 0 | aVideoFrame); |
265 | 0 | } |
266 | | |
267 | | cdm::Status DecryptAndDecodeSamples( |
268 | | const cdm::InputBuffer_2& aEncryptedBuffer, |
269 | | cdm::AudioFrames* aAudioFrames) override |
270 | 0 | { |
271 | 0 | // Handle possible encryption mismatch |
272 | 0 | if (!IsEncryptionSchemeSupported(aEncryptedBuffer.encryption_scheme)) { |
273 | 0 | return cdm::Status::kDecryptError; |
274 | 0 | } |
275 | 0 | |
276 | 0 | return mCDM->DecryptAndDecodeSamples( |
277 | 0 | ConvertToInputBuffer_1(aEncryptedBuffer), aAudioFrames); |
278 | 0 | } |
279 | | |
280 | | void OnPlatformChallengeResponse( |
281 | | const cdm::PlatformChallengeResponse& aResponse) override |
282 | 0 | { |
283 | 0 | mCDM->OnPlatformChallengeResponse(aResponse); |
284 | 0 | } |
285 | | |
286 | | void OnQueryOutputProtectionStatus(cdm::QueryResult aResult, |
287 | | uint32_t aLinkMask, |
288 | | uint32_t aOutputProtectionMask) override |
289 | 0 | { |
290 | 0 | mCDM->OnQueryOutputProtectionStatus( |
291 | 0 | aResult, aLinkMask, aOutputProtectionMask); |
292 | 0 | } |
293 | | |
294 | | void OnStorageId(uint32_t aVersion, |
295 | | const uint8_t* aStorageId, |
296 | | uint32_t aStorageIdSize) override |
297 | 0 | { |
298 | 0 | mCDM->OnStorageId(aVersion, aStorageId, aStorageIdSize); |
299 | 0 | } |
300 | | |
301 | | void Destroy() override |
302 | 0 | { |
303 | 0 | mCDM->Destroy(); |
304 | 0 | delete this; |
305 | 0 | } |
306 | | |
307 | | cdm::ContentDecryptionModule_9* mCDM; |
308 | | cdm::Host_10* mHost; |
309 | | |
310 | | private: |
311 | | // CDM9 supports non-encrypted or cenc encrypted media, anything else should |
312 | | // be rejected. |
313 | | static bool IsEncryptionSchemeSupported( |
314 | | const cdm::EncryptionScheme& aEncryptionScheme) |
315 | 0 | { |
316 | 0 | return aEncryptionScheme == cdm::EncryptionScheme::kUnencrypted || |
317 | 0 | aEncryptionScheme == cdm::EncryptionScheme::kCenc; |
318 | 0 | } |
319 | | |
320 | | // Conversion functions that drop the encryption scheme member. CDMs prior to |
321 | | // 10 assumed no encryption or cenc encryption (if encryption is present). So |
322 | | // we can drop the scheme member if we check to make sure it was one of these |
323 | | // two options. |
324 | | static cdm::InputBuffer_1 ConvertToInputBuffer_1( |
325 | | const cdm::InputBuffer_2& aInputBuffer) |
326 | 0 | { |
327 | 0 | MOZ_ASSERT( |
328 | 0 | IsEncryptionSchemeSupported(aInputBuffer.encryption_scheme), |
329 | 0 | "Encryption scheme should be checked before attempting conversion!"); |
330 | 0 | return { aInputBuffer.data, aInputBuffer.data_size, |
331 | 0 | aInputBuffer.key_id, aInputBuffer.key_id_size, |
332 | 0 | aInputBuffer.iv, aInputBuffer.iv_size, |
333 | 0 | aInputBuffer.subsamples, aInputBuffer.num_subsamples, |
334 | 0 | aInputBuffer.timestamp }; |
335 | 0 | } |
336 | | |
337 | | static cdm::AudioDecoderConfig_1 ConverToAudioDecoderConfig_1( |
338 | | const cdm::AudioDecoderConfig_2& aAudioConfig) |
339 | 0 | { |
340 | 0 | MOZ_ASSERT( |
341 | 0 | IsEncryptionSchemeSupported(aAudioConfig.encryption_scheme), |
342 | 0 | "Encryption scheme should be checked before attempting conversion!"); |
343 | 0 | return { aAudioConfig.codec, |
344 | 0 | aAudioConfig.channel_count, |
345 | 0 | aAudioConfig.bits_per_channel, |
346 | 0 | aAudioConfig.samples_per_second, |
347 | 0 | aAudioConfig.extra_data, |
348 | 0 | aAudioConfig.extra_data_size }; |
349 | 0 | } |
350 | | |
351 | | static cdm::VideoDecoderConfig_1 ConvertToVideoDecoderConfig_1( |
352 | | const cdm::VideoDecoderConfig_2& aVideoConfig) |
353 | 0 | { |
354 | 0 | MOZ_ASSERT( |
355 | 0 | IsEncryptionSchemeSupported(aVideoConfig.encryption_scheme), |
356 | 0 | "Encryption scheme should be checked before attempting conversion!"); |
357 | 0 | return { aVideoConfig.codec, aVideoConfig.profile, |
358 | 0 | aVideoConfig.format, aVideoConfig.coded_size, |
359 | 0 | aVideoConfig.extra_data, aVideoConfig.extra_data_size }; |
360 | 0 | } |
361 | | }; // class ChromiumCDM9BackwardsCompat |
362 | | |
363 | | mozilla::ipc::IPCResult |
364 | | GMPContentChild::RecvPChromiumCDMConstructor(PChromiumCDMChild* aActor) |
365 | 0 | { |
366 | 0 | ChromiumCDMChild* child = static_cast<ChromiumCDMChild*>(aActor); |
367 | 0 | // TODO: Once we support CDM10, create one here, for now try and create CDM9 |
368 | 0 | cdm::Host_9* host9 = child; |
369 | 0 |
|
370 | 0 | void* cdm = nullptr; |
371 | 0 | GMPErr err = mGMPChild->GetAPI(CHROMIUM_CDM_API_BACKWARD_COMPAT, host9, &cdm); |
372 | 0 | if (err != GMPNoErr || !cdm) { |
373 | 0 | NS_WARNING("GMPGetAPI call failed trying to get CDM."); |
374 | 0 | return IPC_FAIL_NO_REASON(this); |
375 | 0 | } |
376 | 0 | cdm::Host_10* host10 = child; |
377 | 0 | cdm = new ChromiumCDM9BackwardsCompat( |
378 | 0 | host10, static_cast<cdm::ContentDecryptionModule_9*>(cdm)); |
379 | 0 |
|
380 | 0 | child->Init(static_cast<cdm::ContentDecryptionModule_10*>(cdm), |
381 | 0 | mGMPChild->mStorageId); |
382 | 0 |
|
383 | 0 | return IPC_OK(); |
384 | 0 | } |
385 | | |
386 | | void |
387 | | GMPContentChild::CloseActive() |
388 | 0 | { |
389 | 0 | // Invalidate and remove any remaining API objects. |
390 | 0 | const ManagedContainer<PGMPVideoDecoderChild>& videoDecoders = |
391 | 0 | ManagedPGMPVideoDecoderChild(); |
392 | 0 | for (auto iter = videoDecoders.ConstIter(); !iter.Done(); iter.Next()) { |
393 | 0 | iter.Get()->GetKey()->SendShutdown(); |
394 | 0 | } |
395 | 0 |
|
396 | 0 | const ManagedContainer<PGMPVideoEncoderChild>& videoEncoders = |
397 | 0 | ManagedPGMPVideoEncoderChild(); |
398 | 0 | for (auto iter = videoEncoders.ConstIter(); !iter.Done(); iter.Next()) { |
399 | 0 | iter.Get()->GetKey()->SendShutdown(); |
400 | 0 | } |
401 | 0 |
|
402 | 0 | const ManagedContainer<PChromiumCDMChild>& cdms = ManagedPChromiumCDMChild(); |
403 | 0 | for (auto iter = cdms.ConstIter(); !iter.Done(); iter.Next()) { |
404 | 0 | iter.Get()->GetKey()->SendShutdown(); |
405 | 0 | } |
406 | 0 | } |
407 | | |
408 | | bool |
409 | | GMPContentChild::IsUsed() |
410 | 0 | { |
411 | 0 | return !ManagedPGMPVideoDecoderChild().IsEmpty() || |
412 | 0 | !ManagedPGMPVideoEncoderChild().IsEmpty() || |
413 | 0 | !ManagedPChromiumCDMChild().IsEmpty(); |
414 | 0 | } |
415 | | |
416 | | } // namespace gmp |
417 | | } // namespace mozilla |