cdm_wrapper.h revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef MEDIA_CDM_PPAPI_CDM_WRAPPER_H_
6#define MEDIA_CDM_PPAPI_CDM_WRAPPER_H_
7
8#include <map>
9#include <queue>
10#include <string>
11
12#include "base/basictypes.h"
13#include "media/cdm/ppapi/api/content_decryption_module.h"
14#include "media/cdm/ppapi/cdm_helpers.h"
15#include "media/cdm/ppapi/supported_cdm_versions.h"
16#include "ppapi/cpp/logging.h"
17
18namespace media {
19
20// CdmWrapper wraps different versions of ContentDecryptionModule interfaces and
21// exposes a common interface to the caller.
22//
23// The caller should call CdmWrapper::Create() to create a CDM instance.
24// CdmWrapper will first try to create a CDM instance that supports the latest
25// CDM interface (ContentDecryptionModule). If such an instance cannot be
26// created (e.g. an older CDM was loaded), CdmWrapper will try to create a CDM
27// that supports an older version of CDM interface (e.g.
28// ContentDecryptionModule_*). Internally CdmWrapper converts the CdmWrapper
29// calls to corresponding ContentDecryptionModule calls.
30//
31// Note that CdmWrapper interface always reflects the latest state of content
32// decryption related PPAPI APIs (e.g. pp::ContentDecryptor_Private).
33//
34// Since this file is highly templated and default implementations are short
35// (just a shim layer in most cases), everything is done in this header file.
36class CdmWrapper {
37 public:
38  static CdmWrapper* Create(const char* key_system,
39                            uint32_t key_system_size,
40                            GetCdmHostFunc get_cdm_host_func,
41                            void* user_data);
42
43  virtual ~CdmWrapper() {};
44
45  // TODO(jrummell): Remove return value when CDM4/5 are removed.
46  virtual bool SetServerCertificate(uint32_t promise_id,
47                                    const uint8_t* server_certificate_data,
48                                    uint32_t server_certificate_data_size) = 0;
49  virtual void CreateSession(uint32_t promise_id,
50                             const char* init_data_type,
51                             uint32_t init_data_type_size,
52                             const uint8_t* init_data,
53                             uint32_t init_data_size,
54                             cdm::SessionType session_type) = 0;
55  virtual void LoadSession(uint32_t promise_id,
56                           const char* web_session_id,
57                           uint32_t web_session_id_size) = 0;
58  virtual void UpdateSession(uint32_t promise_id,
59                             const char* web_session_id,
60                             uint32_t web_session_id_size,
61                             const uint8_t* response,
62                             uint32_t response_size) = 0;
63  // TODO(jrummell): Remove return value when CDM4/5 are removed.
64  virtual bool CloseSession(uint32_t promise_id,
65                            const char* web_session_id,
66                            uint32_t web_session_id_size) = 0;
67  virtual void RemoveSession(uint32_t promise_id,
68                             const char* web_session_id,
69                             uint32_t web_session_id_size) = 0;
70  // TODO(jrummell): Remove return value when CDM4/5 are removed.
71  virtual bool GetUsableKeyIds(uint32_t promise_id,
72                               const char* web_session_id,
73                               uint32_t web_session_id_size) = 0;
74  virtual void TimerExpired(void* context) = 0;
75  virtual cdm::Status Decrypt(const cdm::InputBuffer& encrypted_buffer,
76                              cdm::DecryptedBlock* decrypted_buffer) = 0;
77  virtual cdm::Status InitializeAudioDecoder(
78      const cdm::AudioDecoderConfig& audio_decoder_config) = 0;
79  virtual cdm::Status InitializeVideoDecoder(
80      const cdm::VideoDecoderConfig& video_decoder_config) = 0;
81  virtual void DeinitializeDecoder(cdm::StreamType decoder_type) = 0;
82  virtual void ResetDecoder(cdm::StreamType decoder_type) = 0;
83  virtual cdm::Status DecryptAndDecodeFrame(
84      const cdm::InputBuffer& encrypted_buffer,
85      cdm::VideoFrame* video_frame) = 0;
86  virtual cdm::Status DecryptAndDecodeSamples(
87      const cdm::InputBuffer& encrypted_buffer,
88      cdm::AudioFrames* audio_frames) = 0;
89  virtual void OnPlatformChallengeResponse(
90      const cdm::PlatformChallengeResponse& response) = 0;
91  virtual void OnQueryOutputProtectionStatus(
92      uint32_t link_mask,
93      uint32_t output_protection_mask) = 0;
94
95  // Helper function for the cdm::Host_4 methods. Calls to CreateSession(),
96  // LoadSession(), UpdateSession(), and ReleaseSession() pass in promise ids,
97  // but the CDM interface needs session ids. For create and load, we need to
98  // create a new session_id to pass to the CDM. For update and release, we need
99  // to look up |web_session_id| and convert it into the existing |session_id|.
100  // Since the callbacks don't come through this interface, cdm_adapter needs to
101  // create the mapping (and delete it on release).
102  // TODO(jrummell): Remove these once Host_4 interface is removed.
103  virtual uint32_t LookupPromiseId(uint32_t session_id) = 0;
104  virtual void AssignWebSessionId(uint32_t session_id,
105                                  const char* web_session_id,
106                                  uint32_t web_session_id_size) = 0;
107  virtual std::string LookupWebSessionId(uint32_t session_id) = 0;
108  virtual void DropWebSessionId(std::string web_session_id) = 0;
109
110  // Helper functions for the cdm::Host_4 methods.
111  // CDMs using cdm::Host_6 will call OnSessionUsableKeys() as necessary when
112  // resolving LoadSession() and UpdateSession(). This needs to be simulated
113  // for the older CDMs. These must not be called for cdm::Host_6 and later.
114  // TODO(jrummell): Remove these once Host_4 interface is removed.
115
116  // Query whether a SessionUsableKeys event is necessary for the specified
117  // |promise_id|. Returns true if needed and |web_session_id| is updated,
118  // otherwise returns false.
119  virtual bool SessionUsableKeysEventNeeded(uint32_t promise_id,
120                                            std::string* web_session_id) = 0;
121
122  // Used to indicate that a SessionUsableKeys event is required for the
123  // specified |promise_id| and associated |web_session_id|.
124  virtual void SetSessionUsableKeysEventNeeded(
125      uint32_t promise_id,
126      const char* web_session_id,
127      uint32_t web_session_id_size) = 0;
128
129  // cdm::Host_6 introduces InputBuffer_2 (aka InputBuffer). cdm::Host_4
130  // methods still use InputBuffer_1, so this helper function
131  // converts InputBuffer_2 to InputBuffer_1.
132  // TODO(jrummell): Remove these once Host_4 interfaces is removed.
133  virtual void ConvertInputBuffer(const cdm::InputBuffer& v2,
134                                  cdm::InputBuffer_1* v1) = 0;
135
136  // Prior to CDM_6, |init_data_type| was a content type. This helper convererts
137  // an |init_data_type| to a content type.
138  // TODO(sandersd): Remove once Host_4 interface is removed.
139  virtual std::string ConvertInitDataTypeToContentType(
140      const std::string& init_data_type) const = 0;
141
142 protected:
143  CdmWrapper() {}
144
145 private:
146  DISALLOW_COPY_AND_ASSIGN(CdmWrapper);
147};
148
149// Template class that does the CdmWrapper -> CdmInterface conversion. Default
150// implementations are provided. Any methods that need special treatment should
151// be specialized.
152template <class CdmInterface>
153class CdmWrapperImpl : public CdmWrapper {
154 public:
155  static CdmWrapper* Create(const char* key_system,
156                            uint32_t key_system_size,
157                            GetCdmHostFunc get_cdm_host_func,
158                            void* user_data) {
159    void* cdm_instance = ::CreateCdmInstance(
160        CdmInterface::kVersion, key_system, key_system_size, get_cdm_host_func,
161        user_data);
162    if (!cdm_instance)
163      return NULL;
164
165    return new CdmWrapperImpl<CdmInterface>(
166        static_cast<CdmInterface*>(cdm_instance));
167  }
168
169  virtual ~CdmWrapperImpl() {
170    cdm_->Destroy();
171  }
172
173  // Returns true if |data| is prefixed with |header| and has data after the
174  // |header|.
175  bool HasHeader(const uint8* data,
176                 int data_length,
177                 const std::string& header) {
178    return static_cast<size_t>(data_length) > header.length() &&
179           std::equal(data, data + header.length(), header.begin());
180  }
181
182  virtual bool SetServerCertificate(
183      uint32_t promise_id,
184      const uint8_t* server_certificate_data,
185      uint32_t server_certificate_data_size) OVERRIDE {
186    cdm_->SetServerCertificate(
187        promise_id, server_certificate_data, server_certificate_data_size);
188    return true;
189  }
190
191  virtual void CreateSession(uint32_t promise_id,
192                             const char* init_data_type,
193                             uint32_t init_data_type_size,
194                             const uint8_t* init_data,
195                             uint32_t init_data_size,
196                             cdm::SessionType session_type) OVERRIDE {
197    // TODO(jrummell): Remove this code once |session_type| is passed through
198    // Pepper. When removing, add the header back in for CDM4.
199    PP_DCHECK(session_type == cdm::kTemporary);
200    const char kPersistentSessionHeader[] = "PERSISTENT|";
201    if (HasHeader(init_data, init_data_size, kPersistentSessionHeader)) {
202      cdm_->CreateSession(promise_id,
203                          init_data_type,
204                          init_data_type_size,
205                          init_data + strlen(kPersistentSessionHeader),
206                          init_data_size - strlen(kPersistentSessionHeader),
207                          cdm::kPersistent);
208      return;
209    }
210
211    cdm_->CreateSession(promise_id,
212                        init_data_type,
213                        init_data_type_size,
214                        init_data,
215                        init_data_size,
216                        session_type);
217  }
218
219  virtual void LoadSession(uint32_t promise_id,
220                           const char* web_session_id,
221                           uint32_t web_session_id_size) OVERRIDE {
222    cdm_->LoadSession(promise_id, web_session_id, web_session_id_size);
223  }
224
225  virtual void UpdateSession(uint32_t promise_id,
226                             const char* web_session_id,
227                             uint32_t web_session_id_size,
228                             const uint8_t* response,
229                             uint32_t response_size) OVERRIDE {
230    cdm_->UpdateSession(promise_id,
231                        web_session_id,
232                        web_session_id_size,
233                        response,
234                        response_size);
235  }
236
237  virtual bool CloseSession(uint32_t promise_id,
238                            const char* web_session_id,
239                            uint32_t web_session_id_size) OVERRIDE {
240    cdm_->CloseSession(promise_id, web_session_id, web_session_id_size);
241    return true;
242  }
243
244  virtual void RemoveSession(uint32_t promise_id,
245                             const char* web_session_id,
246                             uint32_t web_session_id_size) OVERRIDE {
247    cdm_->RemoveSession(promise_id, web_session_id, web_session_id_size);
248  }
249
250  virtual bool GetUsableKeyIds(uint32_t promise_id,
251                               const char* web_session_id,
252                               uint32_t web_session_id_size) OVERRIDE {
253    cdm_->GetUsableKeyIds(promise_id, web_session_id, web_session_id_size);
254    return true;
255  }
256
257  virtual void TimerExpired(void* context) OVERRIDE {
258    cdm_->TimerExpired(context);
259  }
260
261  virtual cdm::Status Decrypt(const cdm::InputBuffer& encrypted_buffer,
262                              cdm::DecryptedBlock* decrypted_buffer) OVERRIDE {
263    return cdm_->Decrypt(encrypted_buffer, decrypted_buffer);
264  }
265
266  virtual cdm::Status InitializeAudioDecoder(
267      const cdm::AudioDecoderConfig& audio_decoder_config) OVERRIDE {
268    return cdm_->InitializeAudioDecoder(audio_decoder_config);
269  }
270
271  virtual cdm::Status InitializeVideoDecoder(
272      const cdm::VideoDecoderConfig& video_decoder_config) OVERRIDE {
273    return cdm_->InitializeVideoDecoder(video_decoder_config);
274  }
275
276  virtual void DeinitializeDecoder(cdm::StreamType decoder_type) OVERRIDE {
277    cdm_->DeinitializeDecoder(decoder_type);
278  }
279
280  virtual void ResetDecoder(cdm::StreamType decoder_type) OVERRIDE {
281    cdm_->ResetDecoder(decoder_type);
282  }
283
284  virtual cdm::Status DecryptAndDecodeFrame(
285      const cdm::InputBuffer& encrypted_buffer,
286      cdm::VideoFrame* video_frame) OVERRIDE {
287    return cdm_->DecryptAndDecodeFrame(encrypted_buffer, video_frame);
288  }
289
290  virtual cdm::Status DecryptAndDecodeSamples(
291      const cdm::InputBuffer& encrypted_buffer,
292      cdm::AudioFrames* audio_frames) OVERRIDE {
293    return cdm_->DecryptAndDecodeSamples(encrypted_buffer, audio_frames);
294  }
295
296  virtual void OnPlatformChallengeResponse(
297      const cdm::PlatformChallengeResponse& response) OVERRIDE {
298    cdm_->OnPlatformChallengeResponse(response);
299  }
300
301  virtual void OnQueryOutputProtectionStatus(
302      uint32_t link_mask,
303      uint32_t output_protection_mask) OVERRIDE {
304    cdm_->OnQueryOutputProtectionStatus(link_mask, output_protection_mask);
305  }
306
307  uint32_t CreateSessionId() {
308    return next_session_id_++;
309  }
310
311  void RegisterPromise(uint32_t session_id, uint32_t promise_id) {
312    PP_DCHECK(promise_to_session_id_map_.find(session_id) ==
313              promise_to_session_id_map_.end());
314    promise_to_session_id_map_.insert(std::make_pair(session_id, promise_id));
315  }
316
317  virtual uint32_t LookupPromiseId(uint32_t session_id) {
318    std::map<uint32_t, uint32_t>::iterator it =
319        promise_to_session_id_map_.find(session_id);
320    if (it == promise_to_session_id_map_.end())
321      return 0;
322    uint32_t promise_id = it->second;
323    promise_to_session_id_map_.erase(it);
324    return promise_id;
325  }
326
327  virtual void AssignWebSessionId(uint32_t session_id,
328                                  const char* web_session_id,
329                                  uint32_t web_session_id_size) {
330    web_session_to_session_id_map_.insert(std::make_pair(
331        std::string(web_session_id, web_session_id_size), session_id));
332  }
333
334  uint32_t LookupSessionId(std::string web_session_id) {
335    return web_session_to_session_id_map_.find(web_session_id)->second;
336  }
337
338  virtual std::string LookupWebSessionId(uint32_t session_id) {
339    std::map<std::string, uint32_t>::iterator it;
340    for (it = web_session_to_session_id_map_.begin();
341         it != web_session_to_session_id_map_.end();
342         ++it) {
343      if (it->second == session_id)
344        return it->first;
345    }
346    PP_NOTREACHED();
347    return std::string();
348  }
349
350  virtual void DropWebSessionId(std::string web_session_id) {
351    web_session_to_session_id_map_.erase(web_session_id);
352  }
353
354  virtual bool SessionUsableKeysEventNeeded(uint32_t promise_id,
355                                            std::string* web_session_id) {
356    std::map<uint32_t, std::string>::iterator it =
357        promises_needing_usable_keys_event_.find(promise_id);
358    if (it == promises_needing_usable_keys_event_.end())
359      return false;
360    web_session_id->swap(it->second);
361    promises_needing_usable_keys_event_.erase(it);
362    return true;
363  }
364
365  virtual void SetSessionUsableKeysEventNeeded(uint32_t promise_id,
366                                               const char* web_session_id,
367                                               uint32_t web_session_id_size) {
368    promises_needing_usable_keys_event_.insert(std::make_pair(
369        promise_id, std::string(web_session_id, web_session_id_size)));
370  }
371
372  virtual void ConvertInputBuffer(const cdm::InputBuffer& v2,
373                                  cdm::InputBuffer_1* v1) {
374    v1->data = v2.data;
375    v1->data_size = v2.data_size;
376    v1->data_offset = 0;
377    v1->key_id = v2.key_id;
378    v1->key_id_size = v2.key_id_size;
379    v1->iv = v2.iv;
380    v1->iv_size = v2.iv_size;
381    v1->subsamples = v2.subsamples;
382    v1->num_subsamples = v2.num_subsamples;
383    v1->timestamp = v2.timestamp;
384  }
385
386  virtual std::string ConvertInitDataTypeToContentType(
387      const std::string& init_data_type) const {
388    if (init_data_type == "cenc")
389      return "video/mp4";
390    if (init_data_type == "webm")
391      return "video/webm";
392    return init_data_type;
393  }
394
395 private:
396  CdmWrapperImpl(CdmInterface* cdm) : cdm_(cdm), next_session_id_(100) {
397    PP_DCHECK(cdm_);
398  }
399
400  CdmInterface* cdm_;
401
402  std::map<uint32_t, uint32_t> promise_to_session_id_map_;
403  uint32_t next_session_id_;
404  std::map<std::string, uint32_t> web_session_to_session_id_map_;
405
406  std::map<uint32_t, std::string> promises_needing_usable_keys_event_;
407
408  DISALLOW_COPY_AND_ASSIGN(CdmWrapperImpl);
409};
410
411// Overrides for the cdm::Host_4 methods. Calls to CreateSession(),
412// LoadSession(), UpdateSession(), and ReleaseSession() pass in promise ids,
413// but the CDM interface needs session ids. For create and load, we need to
414// create a new session_id to pass to the CDM. For update and release, we need
415// to look up |web_session_id| and convert it into the existing |session_id|.
416// Since the callbacks don't come through this interface, cdm_adapter needs to
417// create the mapping (and delete it on release). Finally, for create, we need
418// to translate |init_data_type| to a MIME type.
419// TODO(jrummell): Remove these once Host_4 interface is removed.
420
421template <>
422bool CdmWrapperImpl<cdm::ContentDecryptionModule_4>::SetServerCertificate(
423    uint32_t promise_id,
424    const uint8_t* server_certificate_data,
425    uint32_t server_certificate_data_size) {
426  return false;
427}
428
429template <>
430void CdmWrapperImpl<cdm::ContentDecryptionModule_4>::CreateSession(
431    uint32_t promise_id,
432    const char* init_data_type,
433    uint32_t init_data_type_size,
434    const uint8_t* init_data,
435    uint32_t init_data_size,
436    cdm::SessionType session_type) {
437  uint32_t session_id = CreateSessionId();
438  RegisterPromise(session_id, promise_id);
439  std::string converted_init_data_type = ConvertInitDataTypeToContentType(
440      std::string(init_data_type, init_data_type_size));
441  cdm_->CreateSession(session_id,
442                      converted_init_data_type.data(),
443                      converted_init_data_type.length(),
444                      init_data,
445                      init_data_size);
446}
447
448template <>
449void CdmWrapperImpl<cdm::ContentDecryptionModule_4>::LoadSession(
450    uint32_t promise_id,
451    const char* web_session_id,
452    uint32_t web_session_id_size) {
453  uint32_t session_id = CreateSessionId();
454  RegisterPromise(session_id, promise_id);
455  // As CDM_4 doesn't support OnSessionUsableKeysChange(), make sure to generate
456  // one when the promise is resolved. This may be overly aggressive.
457  SetSessionUsableKeysEventNeeded(
458      promise_id, web_session_id, web_session_id_size);
459  cdm_->LoadSession(session_id, web_session_id, web_session_id_size);
460}
461
462template <>
463void CdmWrapperImpl<cdm::ContentDecryptionModule_4>::UpdateSession(
464    uint32_t promise_id,
465    const char* web_session_id,
466    uint32_t web_session_id_size,
467    const uint8_t* response,
468    uint32_t response_size) {
469  std::string web_session_str(web_session_id, web_session_id_size);
470  uint32_t session_id = LookupSessionId(web_session_str);
471  RegisterPromise(session_id, promise_id);
472  // As CDM_4 doesn't support OnSessionUsableKeysChange(), make sure to generate
473  // one when the promise is resolved. This may be overly aggressive.
474  SetSessionUsableKeysEventNeeded(
475      promise_id, web_session_id, web_session_id_size);
476  cdm_->UpdateSession(session_id, response, response_size);
477}
478
479template <>
480bool CdmWrapperImpl<cdm::ContentDecryptionModule_4>::CloseSession(
481    uint32_t promise_id,
482    const char* web_session_id,
483    uint32_t web_session_id_size) {
484  return false;
485}
486
487template <>
488void CdmWrapperImpl<cdm::ContentDecryptionModule_4>::RemoveSession(
489    uint32_t promise_id,
490    const char* web_session_id,
491    uint32_t web_session_id_size) {
492  std::string web_session_str(web_session_id, web_session_id_size);
493  uint32_t session_id = LookupSessionId(web_session_str);
494  RegisterPromise(session_id, promise_id);
495  cdm_->ReleaseSession(session_id);
496}
497
498template <>
499bool CdmWrapperImpl<cdm::ContentDecryptionModule_4>::GetUsableKeyIds(
500    uint32_t promise_id,
501    const char* web_session_id,
502    uint32_t web_session_id_size) {
503  return false;
504}
505
506template <>
507cdm::Status CdmWrapperImpl<cdm::ContentDecryptionModule_4>::Decrypt(
508    const cdm::InputBuffer& encrypted_buffer,
509    cdm::DecryptedBlock* decrypted_buffer) {
510  cdm::InputBuffer_1 buffer;
511  ConvertInputBuffer(encrypted_buffer, &buffer);
512  return cdm_->Decrypt(buffer, decrypted_buffer);
513}
514
515template <>
516cdm::Status
517CdmWrapperImpl<cdm::ContentDecryptionModule_4>::DecryptAndDecodeFrame(
518    const cdm::InputBuffer& encrypted_buffer,
519    cdm::VideoFrame* video_frame) {
520  cdm::InputBuffer_1 buffer;
521  ConvertInputBuffer(encrypted_buffer, &buffer);
522  return cdm_->DecryptAndDecodeFrame(buffer, video_frame);
523}
524
525template <>
526cdm::Status
527CdmWrapperImpl<cdm::ContentDecryptionModule_4>::DecryptAndDecodeSamples(
528    const cdm::InputBuffer& encrypted_buffer,
529    cdm::AudioFrames* audio_frames) {
530  cdm::InputBuffer_1 buffer;
531  ConvertInputBuffer(encrypted_buffer, &buffer);
532  return cdm_->DecryptAndDecodeSamples(buffer, audio_frames);
533}
534
535CdmWrapper* CdmWrapper::Create(const char* key_system,
536                               uint32_t key_system_size,
537                               GetCdmHostFunc get_cdm_host_func,
538                               void* user_data) {
539  COMPILE_ASSERT(cdm::ContentDecryptionModule::kVersion ==
540                     cdm::ContentDecryptionModule_6::kVersion,
541                 update_code_below);
542
543  // Ensure IsSupportedCdmInterfaceVersion() matches this implementation.
544  // Always update this DCHECK when updating this function.
545  // If this check fails, update this function and DCHECK or update
546  // IsSupportedCdmInterfaceVersion().
547  PP_DCHECK(
548      !IsSupportedCdmInterfaceVersion(cdm::ContentDecryptionModule::kVersion +
549                                      1) &&
550      IsSupportedCdmInterfaceVersion(cdm::ContentDecryptionModule::kVersion) &&
551      IsSupportedCdmInterfaceVersion(
552          cdm::ContentDecryptionModule_4::kVersion) &&
553      !IsSupportedCdmInterfaceVersion(cdm::ContentDecryptionModule_4::kVersion -
554                                      1));
555
556  // Try to create the CDM using the latest CDM interface version.
557  CdmWrapper* cdm_wrapper =
558      CdmWrapperImpl<cdm::ContentDecryptionModule>::Create(
559          key_system, key_system_size, get_cdm_host_func, user_data);
560  if (cdm_wrapper)
561    return cdm_wrapper;
562
563  // If |cdm_wrapper| is NULL, try to create the CDM using older supported
564  // versions of the CDM interface.
565  cdm_wrapper = CdmWrapperImpl<cdm::ContentDecryptionModule_4>::Create(
566      key_system, key_system_size, get_cdm_host_func, user_data);
567  return cdm_wrapper;
568}
569
570// When updating the CdmAdapter, ensure you've updated the CdmWrapper to contain
571// stub implementations for new or modified methods that the older CDM interface
572// does not have.
573// Also update supported_cdm_versions.h.
574COMPILE_ASSERT(cdm::ContentDecryptionModule::kVersion ==
575                   cdm::ContentDecryptionModule_6::kVersion,
576               ensure_cdm_wrapper_templates_have_old_version_support);
577
578}  // namespace media
579
580#endif  // MEDIA_CDM_PPAPI_CDM_WRAPPER_H_
581