├── COMPATIBLE ├── docs ├── img │ ├── ocdm_scope.png │ ├── arch_cdm_cdmi.png │ └── arch_layer_approach.png ├── build_notes_chromium.md └── architecture_notes_ocdm.md ├── src ├── com │ ├── README.md │ ├── common │ │ ├── rpc │ │ │ ├── opencdm_callback.x │ │ │ ├── opencdm_callback_xdr.c │ │ │ ├── opencdm_xdr.x │ │ │ ├── opencdm_callback.h │ │ │ ├── opencdm_xdr_clnt.c │ │ │ ├── opencdm_xdr_xdr.c │ │ │ └── opencdm_xdr.h │ │ └── shmemsem │ │ │ ├── shmemsem_helper.h │ │ │ └── shmemsem_helper.cc │ ├── cdm │ │ ├── open_cdm_platform_com_handler_factory.h │ │ └── rpc │ │ │ ├── rpc_cdm_platform_handler.h │ │ │ └── rpc_cdm_platform_handler.cc │ └── mediaengine │ │ └── rpc │ │ ├── rpc_cdm_mediaengine_handler.h │ │ └── rpc_cdm_mediaengine_handler.cc ├── README.md ├── browser │ ├── chrome │ │ ├── tests │ │ │ ├── data │ │ │ │ └── test.html │ │ │ └── ocdm_encrypted_media_istypesupported_browsertest.cc │ │ ├── open_cdm_chrome_common.h │ │ ├── open_cdm.h │ │ └── open_cdm.cc │ └── media_open_cdm.gypi ├── common │ └── open_cdm_common.h ├── mediaengine │ ├── open_cdm_mediaengine_com.h │ ├── open_cdm_mediaengine_impl.h │ ├── open_cdm_mediaengine_factory.h │ ├── open_cdm_mediaengine.h │ └── open_cdm_mediaengine_impl.cc └── cdm │ ├── open_cdm_platform_com.h │ ├── open_cdm_platform_factory.h │ ├── open_cdm_platform_com_callback_receiver.h │ ├── open_cdm_platform_common.h │ ├── open_cdm_platform.h │ ├── open_cdm_platform_impl.h │ └── open_cdm_platform_impl.cc ├── tests └── README.md ├── patch └── add_opencdm_to_chrome_key_system.patch ├── LICENSE └── README.md /COMPATIBLE: -------------------------------------------------------------------------------- 1 | # Compatible Chromium 2 | v43.0.2357.81 3 | -------------------------------------------------------------------------------- /docs/img/ocdm_scope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fraunhoferfokus/open-content-decryption-module/HEAD/docs/img/ocdm_scope.png -------------------------------------------------------------------------------- /docs/img/arch_cdm_cdmi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fraunhoferfokus/open-content-decryption-module/HEAD/docs/img/arch_cdm_cdmi.png -------------------------------------------------------------------------------- /docs/img/arch_layer_approach.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fraunhoferfokus/open-content-decryption-module/HEAD/docs/img/arch_layer_approach.png -------------------------------------------------------------------------------- /src/com/README.md: -------------------------------------------------------------------------------- 1 | # Folder structure 2 | * *cdm*: implementation of content decryption module functionality 3 | * *common*: implementation of common helpers 4 | * *mediaengine*: implementation of media engines -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # Folder structure 2 | * *browser*: browser specific glue code 3 | * *cdm*: content decryption module c++ interfaces 4 | * *com*: implementation of the communication layer 5 | * *common*: configuration files 6 | * *mediaengine*: media engine c++ interfaces -------------------------------------------------------------------------------- /src/browser/chrome/tests/data/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | ## How to test 2 | 3 | ### ...with Pepper Plugin for Chromium 4 | 5 | * Test setup: 6 | * ```$ cd $CHROMIUM_ROOT/src``` 7 | * ```$ ln -s $HOME/opencdm/src/browser/chrome/tests/data $CHROMIUM_ROOT/src/chrome/test/data/media/drmock``` 8 | * In file $CHROMIUM_ROOT/src/chrome/chrome_tests.gypi add the following to the sources section of the target browser_tests 9 | 10 | ``` 11 | 'browser/media/encrypted_media_istypesupported_browsertest.cc', 12 | # OCDM tests 13 | '<(DEPTH)/media/cdm/ppapi/external_open_cdm/browser/chrome/tests/ocdm_encrypted_media_istypesupported_browsertest.cc', 14 | ``` 15 | 16 | * ```$ ./build/gyp_chromium``` 17 | * ```$ ninja -C out/Debug browser_tests``` 18 | 19 | * Test execution: 20 | * ```$ cd $CHROMIUM_ROOT/src``` 21 | * ```$ ./out/Debug/browser_tests --gtest_filter="OpenCDM*"``` 22 | -------------------------------------------------------------------------------- /src/com/common/rpc/opencdm_callback.x: -------------------------------------------------------------------------------- 1 | /* 2 | * OpenCDM XDR to be used for RPC communication between DRM and CDM platform counterpart 3 | * Based on EME methods and naming. 4 | */ 5 | 6 | struct rpc_cb_message { 7 | char session_id <>; 8 | string message <>; 9 | string destination_url <>; 10 | }; 11 | 12 | struct rpc_cb_key_status_update { 13 | char session_id <>; 14 | string message <>; 15 | }; 16 | 17 | 18 | struct rpc_cb_ready { 19 | char session_id <>; 20 | }; 21 | 22 | 23 | struct rpc_cb_error { 24 | char session_id <>; 25 | int error; 26 | string error_message <>; 27 | }; 28 | 29 | program OPEN_CDM_CALLBACK { 30 | version OPEN_CDM_EME_5 { 31 | void ON_KEY_MESSAGE(rpc_cb_message) = 1; 32 | void ON_KEY_READY(rpc_cb_ready) = 2; 33 | void ON_KEY_ERROR(rpc_cb_error) = 3; 34 | void ON_KEY_STATUS_UPDATE(rpc_cb_key_status_update) = 4; 35 | } = 1; 36 | } = 0x66666666; 37 | -------------------------------------------------------------------------------- /src/common/open_cdm_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #ifndef MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_COMMON_OPEN_CDM_COMMON_H_ 15 | #define MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_COMMON_OPEN_CDM_COMMON_H_ 16 | 17 | namespace media { 18 | 19 | //const char kExternalOpenCdmKeySystem[] = "com.opencdm.mockdrm"; 20 | const char kExternalOpenCdmKeySystem[] = "org.chromium.externalclearkey"; 21 | const char kOpenCdmVersion[] = "1.0.0.0"; 22 | 23 | } // namespace media 24 | 25 | #endif // MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_COMMON_OPEN_CDM_COMMON_H_ 26 | -------------------------------------------------------------------------------- /src/mediaengine/open_cdm_mediaengine_com.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #ifndef MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_MEDIAENGINE_OPEN_CDM_MEDIAENGINE_COM_H_ 15 | #define MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_MEDIAENGINE_OPEN_CDM_MEDIAENGINE_COM_H_ 16 | 17 | #include "media/cdm/ppapi/external_open_cdm/mediaengine/open_cdm_mediaengine.h" 18 | 19 | namespace media { 20 | 21 | class OpenCdmMediaengineCom : public OpenCdmMediaengine { 22 | public: 23 | OpenCdmMediaengineCom(char *session_id_val, uint32_t session_id_len, 24 | uint8_t *auth_data_val, uint32_t auth_data_len); 25 | ~OpenCdmMediaengineCom() override {} 26 | 27 | protected: 28 | OpenCdmMediaengineCom() {} 29 | }; 30 | 31 | } // namespace media 32 | 33 | #endif // MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_MEDIAENGINE_OPEN_CDM_MEDIAENGINE_COM_H_ 34 | -------------------------------------------------------------------------------- /src/browser/chrome/open_cdm_chrome_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | // Based on file contibuted to the Chromium project 15 | // media/cdm/ppapi/external_clear_key/clear_key_cdm_common.h 16 | // License notice of original file: 17 | 18 | // Copyright 2013 The Chromium Authors. All rights reserved. 19 | // Use of this source code is governed by a BSD-style license that can be 20 | // found in the LICENSE file. 21 | 22 | #ifndef MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_BROWSER_CHROME_OPEN_CDM_CHROME_COMMON_H_ 23 | #define MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_BROWSER_CHROME_OPEN_CDM_CHROME_COMMON_H_ 24 | 25 | #include "media/cdm/ppapi/api/content_decryption_module.h" 26 | 27 | namespace media { 28 | 29 | // Aliases for the version of the interfaces that this CDM implements. 30 | typedef cdm::ContentDecryptionModule_8 OpenCdmInterface; 31 | typedef OpenCdmInterface::Host OpenCdmHost; 32 | 33 | } // namespace media 34 | 35 | #endif // MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_BROWSER_CHROME_OPEN_CDM_CHROME_COMMON_H_ 36 | -------------------------------------------------------------------------------- /src/cdm/open_cdm_platform_com.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #ifndef MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_CDM_OPEN_CDM_PLATFORM_COM_H_ 15 | #define MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_CDM_OPEN_CDM_PLATFORM_COM_H_ 16 | 17 | #include "media/cdm/ppapi/external_open_cdm/cdm/open_cdm_platform_common.h" 18 | #include "media/cdm/ppapi/external_open_cdm/cdm/open_cdm_platform.h" 19 | #include "media/cdm/ppapi/external_open_cdm/cdm/open_cdm_platform_com_callback_receiver.h" 20 | 21 | #include 22 | 23 | namespace media { 24 | 25 | enum OCDM_COM_STATE { 26 | UNINITIALIZED = 0, 27 | INITIALIZED = 1, 28 | FAULTY = 2, 29 | ABORTED = 3 30 | }; 31 | 32 | class OpenCdmPlatformCom : public OpenCdmPlatform { 33 | public: 34 | ~OpenCdmPlatformCom() override{ 35 | } 36 | OpenCdmPlatformCom(OpenCdmPlatformComCallbackReceiver *callback_receiver_); 37 | 38 | protected: 39 | OpenCdmPlatformCom() { 40 | } 41 | 42 | OCDM_COM_STATE com_state; 43 | 44 | private: 45 | }; 46 | } // namespace media 47 | 48 | #endif // MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_CDM_OPEN_CDM_PLATFORM_COM_H_ 49 | -------------------------------------------------------------------------------- /src/com/common/rpc/opencdm_callback_xdr.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Please do not edit this file. 3 | * It was generated using rpcgen. 4 | */ 5 | 6 | #include "opencdm_callback.h" 7 | 8 | bool_t 9 | xdr_rpc_cb_message (XDR *xdrs, rpc_cb_message *objp) 10 | { 11 | 12 | if (!xdr_array (xdrs, (char **)&objp->session_id.session_id_val, (u_int *) &objp->session_id.session_id_len, ~0, 13 | sizeof (char), (xdrproc_t) xdr_char)) 14 | return FALSE; 15 | if (!xdr_string (xdrs, &objp->message, ~0)) 16 | return FALSE; 17 | if (!xdr_string (xdrs, &objp->destination_url, ~0)) 18 | return FALSE; 19 | return TRUE; 20 | } 21 | 22 | bool_t 23 | xdr_rpc_cb_key_status_update (XDR *xdrs, rpc_cb_key_status_update *objp) 24 | { 25 | 26 | if (!xdr_array (xdrs, (char **)&objp->session_id.session_id_val, (u_int *) &objp->session_id.session_id_len, ~0, 27 | sizeof (char), (xdrproc_t) xdr_char)) 28 | return FALSE; 29 | if (!xdr_string (xdrs, &objp->message, ~0)) 30 | return FALSE; 31 | return TRUE; 32 | } 33 | 34 | bool_t 35 | xdr_rpc_cb_ready (XDR *xdrs, rpc_cb_ready *objp) 36 | { 37 | 38 | if (!xdr_array (xdrs, (char **)&objp->session_id.session_id_val, (u_int *) &objp->session_id.session_id_len, ~0, 39 | sizeof (char), (xdrproc_t) xdr_char)) 40 | return FALSE; 41 | return TRUE; 42 | } 43 | 44 | bool_t 45 | xdr_rpc_cb_error (XDR *xdrs, rpc_cb_error *objp) 46 | { 47 | 48 | if (!xdr_array (xdrs, (char **)&objp->session_id.session_id_val, (u_int *) &objp->session_id.session_id_len, ~0, 49 | sizeof (char), (xdrproc_t) xdr_char)) 50 | return FALSE; 51 | if (!xdr_int (xdrs, &objp->error)) 52 | return FALSE; 53 | if (!xdr_string (xdrs, &objp->error_message, ~0)) 54 | return FALSE; 55 | return TRUE; 56 | } 57 | -------------------------------------------------------------------------------- /src/com/cdm/open_cdm_platform_com_handler_factory.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #ifndef MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_COM_CDM_OPEN_CDM_PLATFORM_COM_HANDLER_FACTORY_H_ 15 | #define MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_COM_CDM_OPEN_CDM_PLATFORM_COM_HANDLER_FACTORY_H_ 16 | 17 | #include "media/cdm/ppapi/external_open_cdm/cdm/open_cdm_platform_com.h" 18 | #include "media/cdm/ppapi/external_open_cdm/cdm/open_cdm_platform_com_callback_receiver.h" 19 | #include "media/cdm/ppapi/external_open_cdm/com/cdm/rpc/rpc_cdm_platform_handler.h" 20 | 21 | namespace media { 22 | 23 | class OpenCdmPlatformComHandlerFactory { 24 | public: 25 | static OpenCdmPlatformCom *Create( 26 | OpenCdmPlatformComCallbackReceiver *callback_receiver_); 27 | }; 28 | 29 | /* 30 | * returns raw pointer to be platform and type independent, can be converted 31 | * to smart pointer on successful creation. 32 | */ 33 | OpenCdmPlatformCom *OpenCdmPlatformComHandlerFactory::Create( 34 | OpenCdmPlatformComCallbackReceiver *callback_receiver_) { 35 | return new RpcCdmPlatformHandler(callback_receiver_); 36 | } 37 | } // namespace media 38 | 39 | #endif // MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_COM_CDM_OPEN_CDM_PLATFORM_COM_HANDLER_FACTORY_H_ 40 | -------------------------------------------------------------------------------- /src/cdm/open_cdm_platform_factory.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #ifndef MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_CDM_OPEN_CDM_PLATFORM_FACTORY_H_ 15 | #define MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_CDM_OPEN_CDM_PLATFORM_FACTORY_H_ 16 | 17 | #include "media/cdm/ppapi/external_open_cdm/cdm/open_cdm_platform.h" 18 | #include "media/cdm/ppapi/external_open_cdm/cdm/open_cdm_platform_impl.h" 19 | #include "media/cdm/ppapi/external_open_cdm/cdm/open_cdm_platform_com_callback_receiver.h" 20 | 21 | namespace media { 22 | 23 | class OpenCdmPlatformInterfaceFactory { 24 | public: 25 | static OpenCdmPlatform *Create( 26 | OpenCdmPlatformComCallbackReceiver *callback_receiver_); 27 | }; 28 | 29 | /* 30 | * returns raw pointer to be platform and type independent, can be converted 31 | * to smart pointer on successful creation. 32 | */ 33 | OpenCdmPlatform *OpenCdmPlatformInterfaceFactory::Create( 34 | OpenCdmPlatformComCallbackReceiver *callback_receiver_) { 35 | // TODO(ska): decide on *CdmImpl by means of platform (unix...) 36 | // and available key systems for given key_system 37 | return new OpenCdmPlatformImpl(callback_receiver_); 38 | } 39 | } // namespace media 40 | 41 | #endif // MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_CDM_OPEN_CDM_PLATFORM_FACTORY_H_ 42 | -------------------------------------------------------------------------------- /src/cdm/open_cdm_platform_com_callback_receiver.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #ifndef MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_CDM_OPEN_CDM_PLATFORM_COM_CALLBACK_RECEIVER_H_ 15 | #define MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_CDM_OPEN_CDM_PLATFORM_COM_CALLBACK_RECEIVER_H_ 16 | 17 | #include 18 | 19 | namespace media { 20 | 21 | /* 22 | * This interface serves as receiver interface for callbacks that are 23 | * necessary to be handled by the platform caller. 24 | */ 25 | class OpenCdmPlatformComCallbackReceiver { 26 | public: 27 | // according to EME 28 | 29 | virtual void ErrorCallback(OpenCdmPlatformSessionId platform_session_id, 30 | uint32_t sys_err, std::string err_msg) = 0; 31 | virtual void MessageCallback(OpenCdmPlatformSessionId platform_session_id, 32 | std::string message, 33 | std::string destination_url) = 0; 34 | virtual void OnKeyStatusUpdateCallback(OpenCdmPlatformSessionId platform_session_id, 35 | std::string message) = 0; 36 | // TODO(ska): message should be uint8_t* 37 | virtual void ReadyCallback(OpenCdmPlatformSessionId platform_session_id) = 0; 38 | 39 | virtual ~OpenCdmPlatformComCallbackReceiver() { 40 | } 41 | protected: 42 | OpenCdmPlatformComCallbackReceiver() { 43 | } 44 | }; 45 | } // namespace media 46 | 47 | #endif // MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_CDM_OPEN_CDM_PLATFORM_COM_CALLBACK_RECEIVER_H_ 48 | -------------------------------------------------------------------------------- /patch/add_opencdm_to_chrome_key_system.patch: -------------------------------------------------------------------------------- 1 | diff --git a/chrome/renderer/media/chrome_key_systems.cc b/chrome/renderer/media/chrome_key_systems.cc 2 | index 63d4734..5f04240 100644 3 | --- a/chrome/renderer/media/chrome_key_systems.cc 4 | +++ b/chrome/renderer/media/chrome_key_systems.cc 5 | @@ -48,6 +48,38 @@ static bool IsPepperCdmAvailable( 6 | return is_available; 7 | } 8 | 9 | +// External Open CDM. 10 | +static void AddExternalOpenCdm( 11 | + std::vector* concrete_key_systems) { 12 | + static const char kExternalOpenCdmKeySystem[] = 13 | + "com.opencdm.mockdrm"; 14 | + static const char kExternalOpenCdmPepperType[] = 15 | + "application/x-ppapi-open-cdm"; 16 | + 17 | + std::vector additional_param_names; 18 | + std::vector additional_param_values; 19 | + if (!IsPepperCdmAvailable(kExternalOpenCdmPepperType, 20 | + &additional_param_names, 21 | + &additional_param_values)) { 22 | + return; 23 | + } 24 | + 25 | + KeySystemInfo info; 26 | + info.key_system = kExternalOpenCdmKeySystem; 27 | + info.supported_codecs |= media::EME_CODEC_WEBM_ALL; 28 | + info.supported_init_data_types |= media::kInitDataTypeMaskWebM; 29 | + #if defined (USE_PROPRIETARY_CODECS) 30 | + info.supported_codecs |= media::EME_CODEC_MP4_ALL; 31 | + info.supported_init_data_types |= media::kInitDataTypeMaskCenc; 32 | + #endif 33 | + 34 | + 35 | + info.pepper_type = kExternalOpenCdmPepperType; 36 | + 37 | + concrete_key_systems->push_back(info); 38 | +} 39 | + 40 | + 41 | // External Clear Key (used for testing). 42 | static void AddExternalClearKey( 43 | std::vector* concrete_key_systems) { 44 | @@ -216,7 +248,7 @@ static void AddPepperBasedWidevine( 45 | void AddChromeKeySystems(std::vector* key_systems_info) { 46 | #if defined(ENABLE_PEPPER_CDMS) 47 | AddExternalClearKey(key_systems_info); 48 | - 49 | + AddExternalOpenCdm(key_systems_info); 50 | #if defined(WIDEVINE_CDM_AVAILABLE) 51 | AddPepperBasedWidevine(key_systems_info); 52 | #endif // defined(WIDEVINE_CDM_AVAILABLE) 53 | -------------------------------------------------------------------------------- /src/com/common/rpc/opencdm_xdr.x: -------------------------------------------------------------------------------- 1 | /* 2 | * OpenCDM XDR to be used for RPC communication between CDM and DRM platform counterpart 3 | * Based on EME methods and naming. 4 | */ 5 | 6 | /* 7 | * REQUEST DATA TYPES 8 | */ 9 | 10 | struct rpc_request_is_type_supported { 11 | char key_system <>; 12 | char mime_type <>; 13 | }; 14 | 15 | struct rpc_request_mediakeys { 16 | char key_system <>; 17 | }; 18 | 19 | struct rpc_request_callback_info { 20 | char hostname <>; 21 | uint64_t prog_num; 22 | uint32_t prog_version; 23 | }; 24 | 25 | struct rpc_request_create_session { 26 | char init_data_type <>; 27 | uint8_t init_data <>; 28 | rpc_request_callback_info callback_info; 29 | }; 30 | 31 | struct rpc_request_load_session { 32 | char session_id <>; 33 | }; 34 | 35 | struct rpc_request_session_update { 36 | char session_id <>; 37 | uint8_t key <>; 38 | }; 39 | 40 | struct rpc_request_session_release { 41 | char session_id <>; 42 | }; 43 | 44 | struct rpc_request_mediaengine_data { 45 | char session_id <>; 46 | uint8_t auth_data <>; 47 | int32_t id_exchange_shmem; 48 | int32_t id_exchange_sem; 49 | }; 50 | 51 | /* 52 | * RESPONSE DATA TYPES 53 | */ 54 | 55 | struct rpc_response_generic { 56 | int platform_val; 57 | }; 58 | 59 | struct rpc_response_create_session { 60 | int platform_val; 61 | char session_id <>; 62 | }; 63 | 64 | program OPEN_CDM { 65 | version OPEN_CDM_EME_5 { 66 | rpc_response_generic RPC_OPEN_CDM_IS_TYPE_SUPPORTED(rpc_request_is_type_supported) = 1; 67 | rpc_response_generic RPC_OPEN_CDM_MEDIAKEYS(rpc_request_mediakeys) = 2; 68 | rpc_response_create_session RPC_OPEN_CDM_MEDIAKEYS_CREATE_SESSION(rpc_request_create_session) = 3; 69 | rpc_response_generic RPC_OPEN_CDM_MEDIAKEYS_LOAD_SESSION(rpc_request_load_session) = 4; 70 | rpc_response_generic RPC_OPEN_CDM_MEDIAKEYSESSION_UPDATE(rpc_request_session_update) = 5; 71 | rpc_response_generic RPC_OPEN_CDM_MEDIAKEYSESSION_RELEASE(rpc_request_session_release) = 6; 72 | rpc_response_generic RPC_OPEN_CDM_MEDIAENGINE(rpc_request_mediaengine_data) = 7; 73 | } = 1; 74 | } = 0x61135687; /* FAMEFHG */ 75 | -------------------------------------------------------------------------------- /src/mediaengine/open_cdm_mediaengine_impl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #ifndef MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_MEDIAENGINE_OPEN_CDM_MEDIAENGINE_IMPL_H_ 15 | #define MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_MEDIAENGINE_OPEN_CDM_MEDIAENGINE_IMPL_H_ 16 | 17 | #include "media/cdm/ppapi/external_open_cdm/mediaengine/open_cdm_mediaengine.h" 18 | #include "media/cdm/ppapi/external_open_cdm/mediaengine/open_cdm_mediaengine_com.h" 19 | #include 20 | #include 21 | #include "media/cdm/ppapi/external_open_cdm/com/common/shmemsem/shmemsem_helper.h" 22 | 23 | namespace media { 24 | 25 | /** 26 | * MediaEngineSession serves as an interface between a platform's mediaengine 27 | * and the CDMi. It is established as soon as EME signals 'keyAdded'. 28 | * RPC and shared memory are used for communication. 29 | * Secure channel decryption is not included. 30 | */ 31 | class OpenCdmMediaengineImpl : public OpenCdmMediaengine { 32 | public: 33 | OpenCdmMediaengineImpl(char *session_id_val, uint32_t session_id_len); 34 | OpenCdmMediaengineImpl(char *session_id_val, uint32_t session_id_len, 35 | uint8_t *auth_data_val, uint32_t auth_data_len); 36 | 37 | // synchronous decryption 38 | DecryptResponse Decrypt(const uint8_t *pbIv, uint32_t cbIv, 39 | const uint8_t *pbData, uint32_t cbData, 40 | uint8_t *out, uint32_t &out_size) override; 41 | 42 | ~OpenCdmMediaengineImpl() override; 43 | private: 44 | OpenCdmMediaengineCom *media_engine_com_; 45 | }; 46 | 47 | } // namespace media 48 | 49 | #endif // MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_MEDIAENGINE_OPEN_CDM_MEDIAENGINE_IMPL_H_ 50 | -------------------------------------------------------------------------------- /src/com/mediaengine/rpc/rpc_cdm_mediaengine_handler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #ifndef MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_COM_MEDIAENGINE_RPC_RPC_CDM_MEDIAENGINE_HANDLER_H_ 15 | #define MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_COM_MEDIAENGINE_RPC_RPC_CDM_MEDIAENGINE_HANDLER_H_ 16 | 17 | #include 18 | #include 19 | 20 | #include "media/cdm/ppapi/external_open_cdm/com/common/shmemsem/shmemsem_helper.h" 21 | #include "media/cdm/ppapi/external_open_cdm/mediaengine/open_cdm_mediaengine_com.h" 22 | 23 | namespace media { 24 | 25 | /** 26 | * MediaEngineSession serves as an interface between a platform's mediaengine 27 | * and the CDMi. It is established as soon as EME signals 'keyAdded'. 28 | * RPC and shared memory are used for communication. 29 | * Secure channel decryption is not included. 30 | */ 31 | class RpcCdmMediaengineHandler : public OpenCdmMediaengineCom { 32 | public: 33 | RpcCdmMediaengineHandler(char *session_id_val, uint32_t session_id_len, 34 | uint8_t *auth_data_val, uint32_t auth_data_len); 35 | DecryptResponse Decrypt(const uint8_t *pbIv, uint32_t cbIv, 36 | const uint8_t *pbData, uint32_t cbData, 37 | uint8_t *out, uint32_t &out_size) override; 38 | //TODO (sph): make out const 39 | ~RpcCdmMediaengineHandler() override; 40 | 41 | private: 42 | MediaEngineSessionId sessionId; 43 | 44 | CLIENT *rpcClient; 45 | 46 | // shared memory 47 | shmem_info *shMemInfo; 48 | int idXchngShMem; 49 | int idXchngSem; 50 | }; 51 | 52 | } // namespace media 53 | 54 | #endif // MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_COM_MEDIAENGINE_RPC_RPC_CDM_MEDIAENGINE_HANDLER_H_ 55 | -------------------------------------------------------------------------------- /src/com/common/shmemsem/shmemsem_helper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | /* 15 | * based on Keith Gaughan - Shared Memory and Semaphores - March 22, 2003 16 | */ 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #ifndef SHMEM_SEM_HELPER 27 | #define SHMEM_SEM_HELPER 28 | 29 | #if !defined(__GNU_LIBRARY__) || defined(_SEM_SEMUN_UNDEFINED) 30 | union semun { 31 | int val; // value for SETVAL 32 | struct semid_ds* buf; // buffer for IPC_STAT, IPC_SET 33 | unsigned short* array; // array for GETALL, SETALL 34 | struct seminfo* __buf; // buffer for IPC_INFO 35 | }; 36 | #endif 37 | 38 | /* 39 | * FAMIUM SPECIFIC 40 | */ 41 | 42 | // static info exchange shmem 43 | struct shmem_info { 44 | int32_t idSidShMem; 45 | int32_t idIvShMem; 46 | int32_t idSampleShMem; 47 | int32_t idSubsampleDataShMem; 48 | uint32_t sidSize; 49 | uint32_t ivSize; 50 | uint32_t sampleSize; 51 | uint32_t subsampleDataSize; 52 | }; 53 | 54 | enum { 55 | SEM_XCHNG_PUSH, 56 | SEM_XCHNG_DECRYPT, 57 | SEM_XCHNG_PULL 58 | }; 59 | 60 | /* 61 | * GENERAL SHARED MEM AND SEMAPHORES 62 | */ 63 | 64 | // Declarations for wrapper functions...line 65 | int AllocateSharedMemory(int n); 66 | void* MapSharedMemory(int id); 67 | void* MapExistingSharedMemory(int id, void* existingAddr); 68 | int DetachExistingSharedMemory(void* existingAddr); 69 | int CreateSemaphoreSet(int n, unsigned short* vals); 70 | void DeleteSemaphoreSet(int id); 71 | void LockSemaphore(int id, int i); 72 | void UnlockSemaphore(int id, int i); 73 | 74 | #endif // SHMEM_SEM_HELPER 75 | -------------------------------------------------------------------------------- /src/mediaengine/open_cdm_mediaengine_factory.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #ifndef MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_MEDIAENGINE_OPEN_CDM_MEDIAENGINE_FACTORY_H_ 15 | #define MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_MEDIAENGINE_OPEN_CDM_MEDIAENGINE_FACTORY_H_ 16 | 17 | #include 18 | 19 | #include "media/cdm/ppapi/external_open_cdm/mediaengine/open_cdm_mediaengine.h" 20 | #include "media/cdm/ppapi/external_open_cdm/mediaengine/open_cdm_mediaengine_impl.h" 21 | 22 | #include "media/cdm/ppapi/cdm_logging.h" 23 | 24 | namespace media { 25 | 26 | //const std::string open_cdm_key_system = "com.opencdm.mockdrm"; 27 | const std::string open_cdm_key_system = "org.chromium.externalclearkey"; 28 | 29 | // TODO(ska): outsource the mapping of key system string 30 | // to mediaengine and platform implementations 31 | 32 | class OpenCdmMediaengineFactory { 33 | public: 34 | static OpenCdmMediaengine *Create(std::string key_system, 35 | OpenCdmPlatformSessionId session_id); 36 | }; 37 | 38 | /* 39 | * returns raw pointer to be platform and type independent, can be converted 40 | * to smart pointer on successful creation. 41 | */ 42 | OpenCdmMediaengine *OpenCdmMediaengineFactory::Create( 43 | std::string key_system, OpenCdmPlatformSessionId session_id) { 44 | if (key_system == open_cdm_key_system) { 45 | CDM_DLOG() << "Instantiate OpenCdmMediaengineImpl!"; 46 | return new OpenCdmMediaengineImpl(session_id.session_id, 47 | session_id.session_id_len); 48 | } else { 49 | CDM_DLOG() << "Failed to create MediaEngine"; 50 | return NULL; 51 | } 52 | } 53 | } // namespace media 54 | 55 | #endif // MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_MEDIAENGINE_OPEN_CDM_MEDIAENGINE_FACTORY_H_ 56 | -------------------------------------------------------------------------------- /src/cdm/open_cdm_platform_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #ifndef MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_CDM_OPEN_CDM_PLATFORM_COMMON_H_ 15 | #define MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_CDM_OPEN_CDM_PLATFORM_COMMON_H_ 16 | 17 | #include 18 | #include "base/basictypes.h" 19 | 20 | namespace media { 21 | 22 | // TODO(sph): PLATFORM_CALL_STATE should not be CDMi, but EME-specific 23 | enum PLATFORM_CALL_STATE { 24 | PLATFORM_CALL_SUCCESS = 0, 25 | PLATFORM_CALL_FAIL = 1, 26 | PLATFORM_CALL_NOACTION = 2, 27 | PLATFORM_CALL_NEEDKEY = 3 28 | }; 29 | 30 | struct PlatformResponse { 31 | PLATFORM_CALL_STATE platform_response; 32 | int32 sys_err; 33 | }; 34 | 35 | struct OpenCdmPlatformSessionId { 36 | char *session_id; // TODO(ska): uint16_t oder uint8_t ? CDMi: 16 37 | uint32_t session_id_len; 38 | }; 39 | 40 | struct MediaKeysResponse : public PlatformResponse { 41 | }; 42 | struct MediaKeysCreateSessionResponse : public PlatformResponse { 43 | OpenCdmPlatformSessionId session_id; 44 | }; 45 | struct MediaKeysLoadSessionResponse : public PlatformResponse { 46 | }; 47 | struct MediaKeySessionUpdateResponse : public PlatformResponse { 48 | }; 49 | struct MediaKeySessionReleaseResponse : public PlatformResponse { 50 | }; 51 | 52 | class DecryptReadyStateKeeper { 53 | public: 54 | virtual ~DecryptReadyStateKeeper(); 55 | static DecryptReadyStateKeeper *getInstance(); 56 | 57 | void lockDecryptSem(); 58 | void unlockDecryptSem(); 59 | void lockInitSem(); 60 | void unlockInitSem(); 61 | private: 62 | DecryptReadyStateKeeper(); 63 | static DecryptReadyStateKeeper *instance; 64 | 65 | int idDecryptReadySem; 66 | int _instance_id; 67 | }; 68 | 69 | } // namespace media 70 | 71 | #endif // MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_CDM_OPEN_CDM_PLATFORM_COMMON_H_ 72 | -------------------------------------------------------------------------------- /src/cdm/open_cdm_platform.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #ifndef MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_CDM_OPEN_CDM_PLATFORM_H_ 15 | #define MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_CDM_OPEN_CDM_PLATFORM_H_ 16 | 17 | #include 18 | #include "media/cdm/ppapi/external_open_cdm/cdm/open_cdm_platform_common.h" 19 | #include "media/cdm/ppapi/external_open_cdm/cdm/open_cdm_platform_com_callback_receiver.h" 20 | 21 | namespace media { 22 | 23 | class OpenCdmPlatform { 24 | public: 25 | // NEW EME based interface 26 | // on errors tear down media keys and media key session objects 27 | 28 | // EME equivalent: new MediaKeys() 29 | virtual MediaKeysResponse MediaKeys(std::string key_system) = 0; 30 | 31 | // EME equivalent: media_keys_.createSession() 32 | virtual MediaKeysCreateSessionResponse MediaKeysCreateSession( 33 | const std::string& init_data_type, const uint8_t* init_data, 34 | int init_data_length) = 0; 35 | 36 | // EME equivalent: media_keys_.loadSession() 37 | virtual MediaKeysLoadSessionResponse MediaKeysLoadSession( 38 | char *session_id_val, uint32_t session_id_len) = 0; 39 | 40 | // EME equivalent: media_key_session_.update() 41 | virtual MediaKeySessionUpdateResponse MediaKeySessionUpdate( 42 | const uint8 *pbKey, uint32 cbKey, char *session_id_val, 43 | uint32_t session_id_len) = 0; 44 | 45 | // EME equivalent: media_key_session_.release() 46 | virtual MediaKeySessionReleaseResponse MediaKeySessionRelease( 47 | char *session_id_val, uint32_t session_id_len) = 0; 48 | 49 | 50 | virtual ~OpenCdmPlatform() { 51 | } 52 | OpenCdmPlatform(OpenCdmPlatformComCallbackReceiver *callback_receiver_); 53 | 54 | protected: 55 | OpenCdmPlatform() { 56 | } 57 | }; 58 | } // namespace media 59 | 60 | #endif // MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_CDM_OPEN_CDM_PLATFORM_H_ 61 | -------------------------------------------------------------------------------- /src/mediaengine/open_cdm_mediaengine.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #ifndef MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_MEDIAENGINE_OPEN_CDM_MEDIAENGINE_H_ 15 | #define MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_MEDIAENGINE_OPEN_CDM_MEDIAENGINE_H_ 16 | 17 | #include "media/cdm/ppapi/external_open_cdm/cdm/open_cdm_platform_common.h" 18 | 19 | namespace media { 20 | 21 | struct MediaEngineSessionResponse : public PlatformResponse { 22 | uint8_t *pbEncryptedSessionKey; 23 | uint32_t cbEncryptedSessionKey; 24 | }; 25 | 26 | struct DecryptResponse : public PlatformResponse { 27 | uint8_t *pbResponseData; 28 | uint32_t cbResponseData; 29 | }; 30 | 31 | /* 32 | * used for encrypting the DRM-decrypted content to ensure a secure 33 | * transmission between CDMi and CDM mediaengine 34 | */ 35 | struct MediaEngineSessionKey { 36 | uint8_t *key; 37 | uint32_t keyLen; 38 | }; 39 | 40 | struct MediaEngineSessionId { 41 | char *id; 42 | uint32_t idLen; 43 | }; 44 | 45 | /* Extended EME: // REVIEW(ska): need better explanation for this 46 | * MediaKeys 47 | * 1*| 48 | * | 49 | * N*| 50 | * MediaKeySession 1*----1* MediaEngineSession 51 | */ 52 | 53 | /* MediaEngineSession is interface only */ 54 | class OpenCdmMediaengine { 55 | public: 56 | OpenCdmMediaengine(char *session_id_val, uint32_t session_id_len, 57 | uint8_t * auth_data_val, uint32_t auth_data_len); 58 | virtual ~OpenCdmMediaengine() { 59 | } 60 | 61 | virtual DecryptResponse Decrypt(const uint8_t *pbIv, uint32_t cbIv, 62 | const uint8_t *pbData, uint32_t cbData, 63 | uint8_t *out, uint32_t &out_size) = 0; 64 | 65 | protected: 66 | OpenCdmMediaengine(char *session_id_val, uint32_t session_id_len); 67 | OpenCdmMediaengine() { 68 | } 69 | }; 70 | 71 | } // namespace media 72 | 73 | #endif // MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_MEDIAENGINE_OPEN_CDM_MEDIAENGINE_H_ 74 | -------------------------------------------------------------------------------- /docs/build_notes_chromium.md: -------------------------------------------------------------------------------- 1 | # Build notes for Chromium 2 | 3 | Chromium supports three types of CDMs: 4 | 5 | * Internal from source (Clear Key, which is implemented internally by aes_decryptor.cc) 6 | * Pepper-based CDMs (used for desktop and Chrome OS) 7 | * Platform-based (e.g. Android using MediaDrm APIs) 8 | 9 | Source: [David Dorwin, Feb 2014](https://groups.google.com/a/chromium.org/d/msg/chromium-dev/exotX6Nf_z0/UtMi4a2sLncJ) 10 | 11 | ## Pepper Plugin API (PPAPI) integration notes 12 | 13 | *Tested with version:* 14 | MAJOR=37 15 | MINOR=0 16 | BUILD= **2041** 17 | PATCH=6 18 | 19 | ## Configuration 20 | 21 | Make sure to have the following gyp variables set before building. 22 | Easiest way is to place a ```~/.gyp/include.gypi``` with contents: 23 | ``` 24 | { 25 | 'variables': { 26 | 'ffmpeg_branding': 'Chrome', 27 | 'proprietary_codecs': 1, 28 | 'enable_pepper_cdms': 1 29 | } 30 | } 31 | ``` 32 | 33 | ## Code hooks 34 | 35 | To make the CDM available in the chromium build the key system needs to be 36 | registered in the file ```$CHROMIUM_ROOT/src/chrome/renderer/media/chrome_key_systems.cc``` 37 | 38 | Similar as for the function ```AddExternalClearKey``` the new key system needs 39 | to be registered by adding and calling the following function: 40 | 41 | ``` 42 | // External Open CDM. 43 | static void AddExternalOpenCdm( 44 | std::vector* concrete_key_systems) { 45 | static const char kExternalOpenCdmKeySystem[] = 46 | "com.opencdm.mockdrm"; 47 | static const char kExternalOpenCdmPepperType[] = 48 | "application/x-ppapi-open-cdm"; 49 | 50 | std::vector additional_param_names; 51 | std::vector additional_param_values; 52 | if (!IsPepperCdmAvailable(kExternalOpenCdmPepperType, 53 | &additional_param_names, 54 | &additional_param_values)) { 55 | return; 56 | } 57 | 58 | KeySystemInfo info(kExternalOpenCdmKeySystem); 59 | 60 | #if defined(USE_PROPRIETARY_CODECS) 61 | info.supported_codecs |= content::EME_CODEC_MP4_ALL; 62 | #endif // defined(USE_PROPRIETARY_CODECS) 63 | 64 | info.pepper_type = kExternalOpenCdmPepperType; 65 | 66 | concrete_key_systems->push_back(info); 67 | } 68 | ``` 69 | 70 | Finally, add the following call into the ```AddChromeKeySystems``` function 71 | subsequent the check if Pepper CDMs are enabled: 72 | 73 | ``` 74 | if defined(ENABLE_PEPPER_CDMS) 75 | AddExternalClearKey(key_systems_info); 76 | AddExternalOpenCdm(key_systems_info); 77 | ``` -------------------------------------------------------------------------------- /src/mediaengine/open_cdm_mediaengine_impl.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #include "media/cdm/ppapi/external_open_cdm/mediaengine/open_cdm_mediaengine_impl.h" 15 | #include "media/cdm/ppapi/external_open_cdm/com/mediaengine/rpc/rpc_cdm_mediaengine_handler.h" 16 | 17 | #include "media/cdm/ppapi/cdm_logging.h" 18 | 19 | namespace media { 20 | 21 | OpenCdmMediaengineImpl::OpenCdmMediaengineImpl(char *session_id_val, 22 | uint32_t session_id_len) { 23 | media_engine_com_ = new RpcCdmMediaengineHandler(session_id_val, 24 | session_id_len, 0, 0); 25 | CDM_DLOG() << "Created new media engine impl "; 26 | } 27 | 28 | OpenCdmMediaengineImpl::OpenCdmMediaengineImpl(char *session_id_val, 29 | uint32_t session_id_len, 30 | uint8_t *auth_data_val, 31 | uint32_t auth_data_len) { 32 | // create media engine session 33 | media_engine_com_ = new RpcCdmMediaengineHandler(session_id_val, 34 | session_id_len, 35 | auth_data_val, 36 | auth_data_len); 37 | } 38 | 39 | OpenCdmMediaengineImpl::~OpenCdmMediaengineImpl() { 40 | CDM_DLOG() << "OpenCdmMediaengineImpl destruct!"; 41 | } 42 | 43 | DecryptResponse OpenCdmMediaengineImpl::Decrypt(const uint8_t *pbIv, 44 | uint32_t cbIv, 45 | const uint8_t *pbData, 46 | uint32_t cbData, uint8_t *out, 47 | uint32_t &out_size) { 48 | printf("_------ Decrypt \n"); 49 | CDM_DLOG() << "OpenCdmMediaengineImpl::Decrypt: "; 50 | DecryptResponse response; 51 | 52 | response = media_engine_com_->Decrypt(pbIv, cbIv, pbData, cbData, out, 53 | out_size); 54 | 55 | return response; 56 | } 57 | 58 | } // namespace media 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Fraunhofer FOKUS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | --------------------------------------------------------------------------- 24 | 25 | Chromium specific parts of the implementation are based on code 26 | contributed to the Chromium project with following copyright notice: 27 | 28 | // Copyright 2014 The Chromium Authors. All rights reserved. 29 | // 30 | // Redistribution and use in source and binary forms, with or without 31 | // modification, are permitted provided that the following conditions are 32 | // met: 33 | // 34 | // * Redistributions of source code must retain the above copyright 35 | // notice, this list of conditions and the following disclaimer. 36 | // * Redistributions in binary form must reproduce the above 37 | // copyright notice, this list of conditions and the following disclaimer 38 | // in the documentation and/or other materials provided with the 39 | // distribution. 40 | // * Neither the name of Google Inc. nor the names of its 41 | // contributors may be used to endorse or promote products derived from 42 | // this software without specific prior written permission. 43 | // 44 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 45 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 46 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 47 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 48 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 49 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 50 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 51 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 52 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 53 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 54 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/cdm/open_cdm_platform_impl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #ifndef MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_CDM_OPEN_CDM_PLATFORM_IMPL_H_ 15 | #define MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_CDM_OPEN_CDM_PLATFORM_IMPL_H_ 16 | 17 | #include 18 | #include "media/cdm/ppapi/external_open_cdm/cdm/open_cdm_platform_common.h" 19 | #include "media/cdm/ppapi/external_open_cdm/cdm/open_cdm_platform_com_callback_receiver.h" 20 | #include "media/cdm/ppapi/external_open_cdm/cdm/open_cdm_platform.h" 21 | #include "media/cdm/ppapi/external_open_cdm/cdm/open_cdm_platform_com.h" 22 | 23 | namespace media { 24 | 25 | class OpenCdmPlatformImpl : public OpenCdmPlatform, 26 | public OpenCdmPlatformComCallbackReceiver { 27 | public: 28 | // NEW EME based interface 29 | // on errors tear down media keys and media key session objects 30 | 31 | // EME equivalent: new MediaKeys() 32 | MediaKeysResponse MediaKeys(std::string key_system) override; 33 | 34 | // EME equivalent: media_keys_.createSession() 35 | MediaKeysCreateSessionResponse MediaKeysCreateSession( 36 | const std::string& init_data_type, const uint8_t* init_data, 37 | int init_data_length) override; 38 | 39 | // EME equivalent: media_keys_.loadSession() 40 | MediaKeysLoadSessionResponse MediaKeysLoadSession( 41 | char *session_id_val, uint32_t session_id_len) override; 42 | 43 | // EME equivalent: media_key_session_.update() 44 | MediaKeySessionUpdateResponse MediaKeySessionUpdate( 45 | const uint8 *pbKey, uint32 cbKey, char *session_id_val, 46 | uint32_t session_id_len) override; 47 | 48 | // EME equivalent: media_key_session_.release() 49 | MediaKeySessionReleaseResponse MediaKeySessionRelease( 50 | char *session_id_val, uint32_t session_id_len) override; 51 | 52 | // OpenCdmComCallbackReceiver inheritance 53 | void ErrorCallback(OpenCdmPlatformSessionId platform_session_id, 54 | uint32_t sys_err, std::string err_msg) override; 55 | void MessageCallback(OpenCdmPlatformSessionId platform_session_id, 56 | std::string message, 57 | std::string destination_url) override; 58 | void OnKeyStatusUpdateCallback(OpenCdmPlatformSessionId platform_session_id, 59 | std::string message) override; 60 | void ReadyCallback(OpenCdmPlatformSessionId platform_session_id) override; 61 | 62 | ~OpenCdmPlatformImpl() override { 63 | } 64 | OpenCdmPlatformImpl( 65 | OpenCdmPlatformComCallbackReceiver *callback_receiver); 66 | 67 | private: 68 | OpenCdmPlatformComCallbackReceiver *callback_receiver_; 69 | OpenCdmPlatformCom *com_handler_; 70 | }; 71 | } // namespace media 72 | 73 | #endif // MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_CDM_OPEN_CDM_PLATFORM_IMPL_H_ 74 | -------------------------------------------------------------------------------- /src/com/common/rpc/opencdm_callback.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Please do not edit this file. 3 | * It was generated using rpcgen. 4 | */ 5 | 6 | #ifndef _OPENCDM_CALLBACK_H_RPCGEN 7 | #define _OPENCDM_CALLBACK_H_RPCGEN 8 | 9 | #include 10 | 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | 17 | struct rpc_cb_message { 18 | struct { 19 | u_int session_id_len; 20 | char *session_id_val; 21 | } session_id; 22 | char *message; 23 | char *destination_url; 24 | }; 25 | typedef struct rpc_cb_message rpc_cb_message; 26 | 27 | struct rpc_cb_key_status_update { 28 | struct { 29 | u_int session_id_len; 30 | char *session_id_val; 31 | } session_id; 32 | char *message; 33 | }; 34 | typedef struct rpc_cb_key_status_update rpc_cb_key_status_update; 35 | 36 | struct rpc_cb_ready { 37 | struct { 38 | u_int session_id_len; 39 | char *session_id_val; 40 | } session_id; 41 | }; 42 | typedef struct rpc_cb_ready rpc_cb_ready; 43 | 44 | struct rpc_cb_error { 45 | struct { 46 | u_int session_id_len; 47 | char *session_id_val; 48 | } session_id; 49 | int error; 50 | char *error_message; 51 | }; 52 | typedef struct rpc_cb_error rpc_cb_error; 53 | 54 | #define OPEN_CDM_CALLBACK 0x66666666 55 | #define OPEN_CDM_EME_5 1 56 | 57 | #if defined(__STDC__) || defined(__cplusplus) 58 | #define ON_KEY_MESSAGE 1 59 | extern void * on_key_message_1(rpc_cb_message *, CLIENT *); 60 | extern void * on_key_message_1_svc(rpc_cb_message *, struct svc_req *); 61 | #define ON_KEY_READY 2 62 | extern void * on_key_ready_1(rpc_cb_ready *, CLIENT *); 63 | extern void * on_key_ready_1_svc(rpc_cb_ready *, struct svc_req *); 64 | #define ON_KEY_ERROR 3 65 | extern void * on_key_error_1(rpc_cb_error *, CLIENT *); 66 | extern void * on_key_error_1_svc(rpc_cb_error *, struct svc_req *); 67 | #define ON_KEY_STATUS_UPDATE 4 68 | extern void * on_key_status_update_1(rpc_cb_key_status_update *, CLIENT *); 69 | extern void * on_key_status_update_1_svc(rpc_cb_key_status_update *, struct svc_req *); 70 | extern int open_cdm_callback_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t); 71 | 72 | #else /* K&R C */ 73 | #define ON_KEY_MESSAGE 1 74 | extern void * on_key_message_1(); 75 | extern void * on_key_message_1_svc(); 76 | #define ON_KEY_READY 2 77 | extern void * on_key_ready_1(); 78 | extern void * on_key_ready_1_svc(); 79 | #define ON_KEY_ERROR 3 80 | extern void * on_key_error_1(); 81 | extern void * on_key_error_1_svc(); 82 | #define ON_KEY_STATUS_UPDATE 4 83 | extern void * on_key_status_update_1(); 84 | extern void * on_key_status_update_1_svc(); 85 | extern int open_cdm_callback_1_freeresult (); 86 | #endif /* K&R C */ 87 | 88 | /* the xdr functions */ 89 | 90 | #if defined(__STDC__) || defined(__cplusplus) 91 | extern bool_t xdr_rpc_cb_message (XDR *, rpc_cb_message*); 92 | extern bool_t xdr_rpc_cb_key_status_update (XDR *, rpc_cb_key_status_update*); 93 | extern bool_t xdr_rpc_cb_ready (XDR *, rpc_cb_ready*); 94 | extern bool_t xdr_rpc_cb_error (XDR *, rpc_cb_error*); 95 | 96 | #else /* K&R C */ 97 | extern bool_t xdr_rpc_cb_message (); 98 | extern bool_t xdr_rpc_cb_key_status_update (); 99 | extern bool_t xdr_rpc_cb_ready (); 100 | extern bool_t xdr_rpc_cb_error (); 101 | 102 | #endif /* K&R C */ 103 | 104 | #ifdef __cplusplus 105 | } 106 | #endif 107 | 108 | #endif /* !_OPENCDM_CALLBACK_H_RPCGEN */ 109 | -------------------------------------------------------------------------------- /src/com/cdm/rpc/rpc_cdm_platform_handler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #ifndef MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_COM_CDM_RPC_RPC_CDM_PLATFORM_HANDLER_H_ 15 | #define MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_COM_CDM_RPC_RPC_CDM_PLATFORM_HANDLER_H_ 16 | 17 | #include 18 | #include 19 | 20 | #include "media/cdm/ppapi/external_open_cdm/cdm/open_cdm_platform_common.h" 21 | #include "media/cdm/ppapi/external_open_cdm/cdm/open_cdm_platform_com.h" 22 | #include "media/cdm/ppapi/external_open_cdm/cdm/open_cdm_platform_com_callback_receiver.h" 23 | #include "media/cdm/ppapi/external_open_cdm/com/common/rpc/opencdm_callback.h" 24 | 25 | namespace media { 26 | 27 | class RpcCdmPlatformHandler : public OpenCdmPlatformCom { 28 | public: 29 | // NEW EME based interface 30 | // on errors tear down media keys and media key session objects 31 | 32 | // EME equivalent: new MediaKeys() 33 | MediaKeysResponse MediaKeys(std::string key_system) override; 34 | 35 | // EME equivalent: media_keys_.createSession() 36 | MediaKeysCreateSessionResponse MediaKeysCreateSession( 37 | const std::string& init_data_type, const uint8_t* init_data, 38 | int init_data_length) override; 39 | 40 | // EME equivalent: media_keys_.loadSession() 41 | MediaKeysLoadSessionResponse MediaKeysLoadSession( 42 | char *session_id_val, uint32_t session_id_len) override; 43 | 44 | // EME equivalent: media_key_session_.update() 45 | MediaKeySessionUpdateResponse MediaKeySessionUpdate( 46 | const uint8 *pbKey, uint32 cbKey, char *session_id_val, 47 | uint32_t session_id_len) override; 48 | 49 | // EME equivalent: media_key_session_.release() 50 | MediaKeySessionReleaseResponse MediaKeySessionRelease( 51 | char *session_id_val, uint32_t session_id_len) override; 52 | 53 | ~RpcCdmPlatformHandler() override { 54 | } 55 | RpcCdmPlatformHandler(OpenCdmPlatformComCallbackReceiver *callback_receiver); 56 | 57 | // RPC specific behavior 58 | static void * DelegateRpcInit(void *call_params); 59 | static void DelegateRpcCallback(struct svc_req *rqstp, 60 | register SVCXPRT *transp); 61 | static void OnMessage1SvcDelegate(rpc_cb_message *kmm, 62 | struct svc_req *rqstp, RpcCdmPlatformHandler *p_instance); 63 | static void OnReady1SvcDelegate(rpc_cb_ready *keyready_param, 64 | struct svc_req *rqstp, RpcCdmPlatformHandler *p_instance); 65 | static void OnError1SvcDelegate(rpc_cb_error *err_param, 66 | struct svc_req *rqstp, RpcCdmPlatformHandler *p_instance); 67 | static void OnKeyStatusUpdate1SvcDelegate( 68 | rpc_cb_key_status_update *kmm, struct svc_req *rqstp, 69 | RpcCdmPlatformHandler *p_instance); 70 | 71 | private: 72 | OpenCdmPlatformComCallbackReceiver *callback_receiver_; 73 | // TODO(ska): remove, when this gets available in callbacks 74 | 75 | void *RpcInitPrivate(void *thread_parm); 76 | void RpcCallbackPrivate(struct svc_req *rqstp, register SVCXPRT *transp); 77 | void OnMessage1Svc(rpc_cb_message *, struct svc_req *); 78 | void OnReady1Svc(rpc_cb_ready *, struct svc_req *); 79 | void OnError1Svc(rpc_cb_error *err_param, struct svc_req *rqstp); 80 | 81 | std::string rpc_server_host; 82 | CLIENT *rpc_client; 83 | int rpc_prog; 84 | }; 85 | } // namespace media 86 | 87 | #endif // MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_COM_CDM_RPC_RPC_CDM_PLATFORM_HANDLER_H_ 88 | -------------------------------------------------------------------------------- /src/com/common/rpc/opencdm_xdr_clnt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Please do not edit this file. 3 | * It was generated using rpcgen. 4 | */ 5 | 6 | #include /* for memset */ 7 | #include "opencdm_xdr.h" 8 | 9 | /* Default timeout can be changed using clnt_control() */ 10 | static struct timeval TIMEOUT = { 25, 0 }; 11 | 12 | rpc_response_generic * 13 | rpc_open_cdm_is_type_supported_1(rpc_request_is_type_supported *argp, CLIENT *clnt) 14 | { 15 | static rpc_response_generic clnt_res; 16 | 17 | memset((char *)&clnt_res, 0, sizeof(clnt_res)); 18 | if (clnt_call (clnt, RPC_OPEN_CDM_IS_TYPE_SUPPORTED, 19 | (xdrproc_t) xdr_rpc_request_is_type_supported, (caddr_t) argp, 20 | (xdrproc_t) xdr_rpc_response_generic, (caddr_t) &clnt_res, 21 | TIMEOUT) != RPC_SUCCESS) { 22 | return (NULL); 23 | } 24 | return (&clnt_res); 25 | } 26 | 27 | rpc_response_generic * 28 | rpc_open_cdm_mediakeys_1(rpc_request_mediakeys *argp, CLIENT *clnt) 29 | { 30 | static rpc_response_generic clnt_res; 31 | 32 | memset((char *)&clnt_res, 0, sizeof(clnt_res)); 33 | if (clnt_call (clnt, RPC_OPEN_CDM_MEDIAKEYS, 34 | (xdrproc_t) xdr_rpc_request_mediakeys, (caddr_t) argp, 35 | (xdrproc_t) xdr_rpc_response_generic, (caddr_t) &clnt_res, 36 | TIMEOUT) != RPC_SUCCESS) { 37 | return (NULL); 38 | } 39 | return (&clnt_res); 40 | } 41 | 42 | rpc_response_create_session * 43 | rpc_open_cdm_mediakeys_create_session_1(rpc_request_create_session *argp, CLIENT *clnt) 44 | { 45 | static rpc_response_create_session clnt_res; 46 | 47 | memset((char *)&clnt_res, 0, sizeof(clnt_res)); 48 | if (clnt_call (clnt, RPC_OPEN_CDM_MEDIAKEYS_CREATE_SESSION, 49 | (xdrproc_t) xdr_rpc_request_create_session, (caddr_t) argp, 50 | (xdrproc_t) xdr_rpc_response_create_session, (caddr_t) &clnt_res, 51 | TIMEOUT) != RPC_SUCCESS) { 52 | return (NULL); 53 | } 54 | return (&clnt_res); 55 | } 56 | 57 | rpc_response_generic * 58 | rpc_open_cdm_mediakeys_load_session_1(rpc_request_load_session *argp, CLIENT *clnt) 59 | { 60 | static rpc_response_generic clnt_res; 61 | 62 | memset((char *)&clnt_res, 0, sizeof(clnt_res)); 63 | if (clnt_call (clnt, RPC_OPEN_CDM_MEDIAKEYS_LOAD_SESSION, 64 | (xdrproc_t) xdr_rpc_request_load_session, (caddr_t) argp, 65 | (xdrproc_t) xdr_rpc_response_generic, (caddr_t) &clnt_res, 66 | TIMEOUT) != RPC_SUCCESS) { 67 | return (NULL); 68 | } 69 | return (&clnt_res); 70 | } 71 | 72 | rpc_response_generic * 73 | rpc_open_cdm_mediakeysession_update_1(rpc_request_session_update *argp, CLIENT *clnt) 74 | { 75 | static rpc_response_generic clnt_res; 76 | 77 | memset((char *)&clnt_res, 0, sizeof(clnt_res)); 78 | if (clnt_call (clnt, RPC_OPEN_CDM_MEDIAKEYSESSION_UPDATE, 79 | (xdrproc_t) xdr_rpc_request_session_update, (caddr_t) argp, 80 | (xdrproc_t) xdr_rpc_response_generic, (caddr_t) &clnt_res, 81 | TIMEOUT) != RPC_SUCCESS) { 82 | return (NULL); 83 | } 84 | return (&clnt_res); 85 | } 86 | 87 | rpc_response_generic * 88 | rpc_open_cdm_mediakeysession_release_1(rpc_request_session_release *argp, CLIENT *clnt) 89 | { 90 | static rpc_response_generic clnt_res; 91 | 92 | memset((char *)&clnt_res, 0, sizeof(clnt_res)); 93 | if (clnt_call (clnt, RPC_OPEN_CDM_MEDIAKEYSESSION_RELEASE, 94 | (xdrproc_t) xdr_rpc_request_session_release, (caddr_t) argp, 95 | (xdrproc_t) xdr_rpc_response_generic, (caddr_t) &clnt_res, 96 | TIMEOUT) != RPC_SUCCESS) { 97 | return (NULL); 98 | } 99 | return (&clnt_res); 100 | } 101 | 102 | rpc_response_generic * 103 | rpc_open_cdm_mediaengine_1(rpc_request_mediaengine_data *argp, CLIENT *clnt) 104 | { 105 | static rpc_response_generic clnt_res; 106 | 107 | memset((char *)&clnt_res, 0, sizeof(clnt_res)); 108 | if (clnt_call (clnt, RPC_OPEN_CDM_MEDIAENGINE, 109 | (xdrproc_t) xdr_rpc_request_mediaengine_data, (caddr_t) argp, 110 | (xdrproc_t) xdr_rpc_response_generic, (caddr_t) &clnt_res, 111 | TIMEOUT) != RPC_SUCCESS) { 112 | return (NULL); 113 | } 114 | return (&clnt_res); 115 | } 116 | -------------------------------------------------------------------------------- /src/cdm/open_cdm_platform_impl.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #include "media/cdm/ppapi/external_open_cdm/cdm/open_cdm_platform_impl.h" 15 | #include "media/cdm/ppapi/external_open_cdm/com/cdm/open_cdm_platform_com_handler_factory.h" 16 | #include "media/cdm/ppapi/cdm_logging.h" 17 | 18 | namespace media { 19 | 20 | OpenCdmPlatformImpl::OpenCdmPlatformImpl( 21 | OpenCdmPlatformComCallbackReceiver *callback_receiver) 22 | : callback_receiver_(callback_receiver) { 23 | CDM_DLOG() << "new OpenCdmPlatformCdmiImpl instance"; 24 | com_handler_ = OpenCdmPlatformComHandlerFactory::Create(this); 25 | } 26 | 27 | MediaKeysResponse OpenCdmPlatformImpl::MediaKeys(std::string key_system) { 28 | CDM_DLOG() << "OpenCdmPlatformCdmiImpl::MediaKeys"; 29 | MediaKeysResponse response = com_handler_->MediaKeys(key_system); 30 | 31 | return response; 32 | } 33 | 34 | MediaKeysCreateSessionResponse OpenCdmPlatformImpl::MediaKeysCreateSession( 35 | const std::string& init_data_type, const uint8_t* init_data, 36 | int init_data_length) { 37 | CDM_DLOG() << "OpenCdmPlatformCdmiImpl::MediaKeysCreateSession"; 38 | MediaKeysCreateSessionResponse response; 39 | 40 | response = com_handler_->MediaKeysCreateSession(init_data_type, init_data, 41 | init_data_length); 42 | 43 | return response; 44 | } 45 | 46 | MediaKeysLoadSessionResponse OpenCdmPlatformImpl::MediaKeysLoadSession( 47 | char *session_id_val, uint32_t session_id_len) { 48 | CDM_DLOG() << "OpenCdmPlatformCdmiImpl::MediaKeysLoadSession"; 49 | MediaKeysLoadSessionResponse response; 50 | 51 | response = com_handler_->MediaKeysLoadSession(session_id_val, session_id_len); 52 | 53 | return response; 54 | } 55 | 56 | MediaKeySessionUpdateResponse OpenCdmPlatformImpl::MediaKeySessionUpdate( 57 | const uint8 *pbKey, uint32 cbKey, char *session_id_val, 58 | uint32_t session_id_len) { 59 | CDM_DLOG() << "OpenCdmPlatformCdmiImpl::MediaKeySessionUpdate"; 60 | MediaKeySessionUpdateResponse response; 61 | 62 | response = com_handler_->MediaKeySessionUpdate(pbKey, cbKey, session_id_val, 63 | session_id_len); 64 | 65 | return response; 66 | } 67 | 68 | MediaKeySessionReleaseResponse OpenCdmPlatformImpl::MediaKeySessionRelease( 69 | char *session_id_val, uint32_t session_id_len) { 70 | CDM_DLOG() << "OpenCdmPlatformCdmiImpl::MediaKeySessionRelease"; 71 | MediaKeySessionReleaseResponse response; 72 | 73 | response = com_handler_->MediaKeySessionRelease(session_id_val, 74 | session_id_len); 75 | 76 | return response; 77 | } 78 | 79 | // OpenCdmComCallbackReceiver inheritance 80 | void OpenCdmPlatformImpl::ErrorCallback( 81 | OpenCdmPlatformSessionId platform_session_id, uint32_t sys_err, 82 | std::string err_msg) { 83 | callback_receiver_->ErrorCallback(platform_session_id, sys_err, err_msg); 84 | } 85 | 86 | void OpenCdmPlatformImpl::MessageCallback( 87 | OpenCdmPlatformSessionId platform_session_id, std::string message, 88 | std::string destination_url) { 89 | callback_receiver_->MessageCallback(platform_session_id, message, 90 | destination_url); 91 | } 92 | 93 | void OpenCdmPlatformImpl::OnKeyStatusUpdateCallback( 94 | OpenCdmPlatformSessionId platform_session_id, std::string message 95 | ) { 96 | callback_receiver_->OnKeyStatusUpdateCallback(platform_session_id, message); 97 | } 98 | void OpenCdmPlatformImpl::ReadyCallback( 99 | OpenCdmPlatformSessionId platform_session_id) { 100 | callback_receiver_->ReadyCallback(platform_session_id); 101 | } 102 | 103 | } // namespace media 104 | -------------------------------------------------------------------------------- /src/com/common/shmemsem/shmemsem_helper.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | /* 15 | * based on Keith Gaughan - Shared Memory and Semaphores - March 22, 2003 16 | */ 17 | #include "media/cdm/ppapi/external_open_cdm/com/common/shmemsem/shmemsem_helper.h" 18 | 19 | /** 20 | * Allocates a shared memory segment. 21 | * 22 | * @param n Size (in bytes) of chunk to allocate. 23 | * @return Id of shared memory chunk. 24 | */ 25 | int AllocateSharedMemory(int n) 26 | { 27 | assert(n > 0); // Idiot-proof the call. 28 | return shmget(IPC_PRIVATE, n, IPC_CREAT | SHM_R | SHM_W); 29 | } 30 | 31 | /** 32 | * Maps a shared memory segment onto our address space. 33 | * 34 | * @param id Shared memory block to map. 35 | * @return Address of mapped block. 36 | */ 37 | void* MapSharedMemory(int id) 38 | { 39 | void* addr; 40 | assert(id != 0); // Idiot-proof the call. 41 | addr = shmat(id, NULL, 0); // Attach the segment... 42 | shmctl(id, IPC_RMID, NULL); // ...and mark it destroyed. 43 | return addr; 44 | } 45 | 46 | /** 47 | * Maps a shared memory segment onto our address space. 48 | * 49 | * @param id Shared memory block to map. 50 | * @param existingAddr Adress of memory block to be shared. 51 | * @return Address of mapped block. 52 | */ 53 | void* MapExistingSharedMemory(int id, void* existingAddr) 54 | { 55 | void* addr; 56 | assert(id != 0); // Idiot-proof the call. 57 | addr = shmat(id, existingAddr, 0); // Attach the segment... 58 | shmctl(id, IPC_RMID, NULL); // ...and mark it destroyed. 59 | return addr; 60 | } 61 | 62 | /** 63 | * Detaches a shared memory segment 64 | * 65 | * @param id Shared memory block to map. 66 | * @param existingAddr Adress of memory block to be shared. 67 | * @return Address of mapped block. 68 | */ 69 | int DetachExistingSharedMemory(void* existingAddr) 70 | { 71 | int id = shmdt(existingAddr); // Detach the segment... 72 | return id; 73 | } 74 | 75 | /** 76 | * Creates a new semaphore set. 77 | * 78 | * @param n Number of semaphores in set. 79 | * @param vals Default values to start off with. 80 | * @return Id of semaphore set. 81 | */ 82 | int CreateSemaphoreSet(int n, unsigned short* vals) 83 | { 84 | union semun arg; 85 | int id; 86 | assert(n > 0); // You need at least one! 87 | assert(vals != NULL); // And they need initialising! 88 | id = semget(IPC_PRIVATE, n, SHM_R | SHM_W); 89 | arg.array = vals; 90 | //int ctl = semctl(id, 0, SETALL, arg); 91 | semctl(id, 0, SETALL, arg); 92 | return id; 93 | } 94 | 95 | /** 96 | * Frees up the given semaphore set. 97 | * 98 | * @param id Id of the semaphore group. 99 | */ 100 | void DeleteSemaphoreSet(int id) 101 | { 102 | if (semctl(id, 0, IPC_RMID, 0) == -1) 103 | { 104 | perror("Error releasing semaphore!"); 105 | exit(EXIT_FAILURE); 106 | } 107 | } 108 | 109 | /** 110 | * Locks a semaphore within a semaphore set. 111 | * 112 | * @param id Semaphore set it belongs to. 113 | * @param i Actual semaphore to lock. 114 | * 115 | * @note If it’s already locked, you’re put to sleep. 116 | */ 117 | void LockSemaphore(int id, int i) 118 | { 119 | struct sembuf sb; 120 | sb.sem_num = i; 121 | sb.sem_op = -1; 122 | sb.sem_flg = SEM_UNDO; 123 | semop(id, &sb, 1); 124 | } 125 | 126 | /** 127 | * Unlocks a semaphore within a semaphore set. 128 | * 129 | * @param id Semaphore set it belongs to. 130 | * @param i Actual semaphore to unlock. 131 | */ 132 | void UnlockSemaphore(int id, int i) 133 | { 134 | struct sembuf sb; 135 | sb.sem_num = i; 136 | sb.sem_op = 1; 137 | sb.sem_flg = SEM_UNDO; 138 | semop(id, &sb, 1); 139 | } 140 | -------------------------------------------------------------------------------- /src/com/common/rpc/opencdm_xdr_xdr.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Please do not edit this file. 3 | * It was generated using rpcgen. 4 | */ 5 | 6 | #include "opencdm_xdr.h" 7 | 8 | bool_t 9 | xdr_rpc_request_is_type_supported (XDR *xdrs, rpc_request_is_type_supported *objp) 10 | { 11 | 12 | if (!xdr_array (xdrs, (char **)&objp->key_system.key_system_val, (u_int *) &objp->key_system.key_system_len, ~0, 13 | sizeof (char), (xdrproc_t) xdr_char)) 14 | return FALSE; 15 | if (!xdr_array (xdrs, (char **)&objp->mime_type.mime_type_val, (u_int *) &objp->mime_type.mime_type_len, ~0, 16 | sizeof (char), (xdrproc_t) xdr_char)) 17 | return FALSE; 18 | return TRUE; 19 | } 20 | 21 | bool_t 22 | xdr_rpc_request_mediakeys (XDR *xdrs, rpc_request_mediakeys *objp) 23 | { 24 | 25 | if (!xdr_array (xdrs, (char **)&objp->key_system.key_system_val, (u_int *) &objp->key_system.key_system_len, ~0, 26 | sizeof (char), (xdrproc_t) xdr_char)) 27 | return FALSE; 28 | return TRUE; 29 | } 30 | 31 | bool_t 32 | xdr_rpc_request_callback_info (XDR *xdrs, rpc_request_callback_info *objp) 33 | { 34 | 35 | if (!xdr_array (xdrs, (char **)&objp->hostname.hostname_val, (u_int *) &objp->hostname.hostname_len, ~0, 36 | sizeof (char), (xdrproc_t) xdr_char)) 37 | return FALSE; 38 | if (!xdr_uint64_t (xdrs, &objp->prog_num)) 39 | return FALSE; 40 | if (!xdr_uint32_t (xdrs, &objp->prog_version)) 41 | return FALSE; 42 | return TRUE; 43 | } 44 | 45 | bool_t 46 | xdr_rpc_request_create_session (XDR *xdrs, rpc_request_create_session *objp) 47 | { 48 | 49 | if (!xdr_array (xdrs, (char **)&objp->init_data_type.init_data_type_val, (u_int *) &objp->init_data_type.init_data_type_len, ~0, 50 | sizeof (char), (xdrproc_t) xdr_char)) 51 | return FALSE; 52 | if (!xdr_array (xdrs, (char **)&objp->init_data.init_data_val, (u_int *) &objp->init_data.init_data_len, ~0, 53 | sizeof (uint8_t), (xdrproc_t) xdr_uint8_t)) 54 | return FALSE; 55 | if (!xdr_rpc_request_callback_info (xdrs, &objp->callback_info)) 56 | return FALSE; 57 | return TRUE; 58 | } 59 | 60 | bool_t 61 | xdr_rpc_request_load_session (XDR *xdrs, rpc_request_load_session *objp) 62 | { 63 | 64 | if (!xdr_array (xdrs, (char **)&objp->session_id.session_id_val, (u_int *) &objp->session_id.session_id_len, ~0, 65 | sizeof (char), (xdrproc_t) xdr_char)) 66 | return FALSE; 67 | return TRUE; 68 | } 69 | 70 | bool_t 71 | xdr_rpc_request_session_update (XDR *xdrs, rpc_request_session_update *objp) 72 | { 73 | 74 | if (!xdr_array (xdrs, (char **)&objp->session_id.session_id_val, (u_int *) &objp->session_id.session_id_len, ~0, 75 | sizeof (char), (xdrproc_t) xdr_char)) 76 | return FALSE; 77 | if (!xdr_array (xdrs, (char **)&objp->key.key_val, (u_int *) &objp->key.key_len, ~0, 78 | sizeof (uint8_t), (xdrproc_t) xdr_uint8_t)) 79 | return FALSE; 80 | return TRUE; 81 | } 82 | 83 | bool_t 84 | xdr_rpc_request_session_release (XDR *xdrs, rpc_request_session_release *objp) 85 | { 86 | 87 | if (!xdr_array (xdrs, (char **)&objp->session_id.session_id_val, (u_int *) &objp->session_id.session_id_len, ~0, 88 | sizeof (char), (xdrproc_t) xdr_char)) 89 | return FALSE; 90 | return TRUE; 91 | } 92 | 93 | bool_t 94 | xdr_rpc_request_mediaengine_data (XDR *xdrs, rpc_request_mediaengine_data *objp) 95 | { 96 | 97 | if (!xdr_array (xdrs, (char **)&objp->session_id.session_id_val, (u_int *) &objp->session_id.session_id_len, ~0, 98 | sizeof (char), (xdrproc_t) xdr_char)) 99 | return FALSE; 100 | if (!xdr_array (xdrs, (char **)&objp->auth_data.auth_data_val, (u_int *) &objp->auth_data.auth_data_len, ~0, 101 | sizeof (uint8_t), (xdrproc_t) xdr_uint8_t)) 102 | return FALSE; 103 | if (!xdr_int32_t (xdrs, &objp->id_exchange_shmem)) 104 | return FALSE; 105 | if (!xdr_int32_t (xdrs, &objp->id_exchange_sem)) 106 | return FALSE; 107 | return TRUE; 108 | } 109 | 110 | bool_t 111 | xdr_rpc_response_generic (XDR *xdrs, rpc_response_generic *objp) 112 | { 113 | 114 | if (!xdr_int (xdrs, &objp->platform_val)) 115 | return FALSE; 116 | return TRUE; 117 | } 118 | 119 | bool_t 120 | xdr_rpc_response_create_session (XDR *xdrs, rpc_response_create_session *objp) 121 | { 122 | 123 | if (!xdr_int (xdrs, &objp->platform_val)) 124 | return FALSE; 125 | if (!xdr_array (xdrs, (char **)&objp->session_id.session_id_val, (u_int *) &objp->session_id.session_id_len, ~0, 126 | sizeof (char), (xdrproc_t) xdr_char)) 127 | return FALSE; 128 | return TRUE; 129 | } 130 | -------------------------------------------------------------------------------- /docs/architecture_notes_ocdm.md: -------------------------------------------------------------------------------- 1 | # Architecture notes OCDM 2 | 3 | As described in the main [README.md](../README.md) file, the scope of OCDM includes the CDM itself and the communication interfaces for CDM and media engine. 4 | 5 | ![Scope of OCDM](https://raw.githubusercontent.com/fraunhoferfokus/open-content-decryption-module/master/docs/img/ocdm_scope.png "Scope of OCDM") 6 | 7 | In order to support multiple browser environments and multiple DRM platforms at the same time the architecture of the OCDM has been designed using three different abstraction layers. This is described in the [3-layer architecture section](#3-layer architecture). 8 | 9 | The architecture can be extended with a CDMi component to allow for an interoperable DRM architecture on platform level as well. There is also a corresponding OCDMi GitHub project available. How OCDM can be used with together with the OCDMi and CDMi compatible DRM systems is described in [CDM and CDMi](#CDM and CDMi). 10 | 11 | ## 3-layer architecture 12 | 13 | Besides the division into the two components, CDM and media engine, OCDM is divided into three logical layers: browser glue, core and communication. The OCDM implementation is strictly bound to these layers by specific interfaces, which enables flexible exchange of a specific layer implementation with another one. 14 | 15 | ![OCDM 3-layer architecture](https://raw.githubusercontent.com/fraunhoferfokus/open-content-decryption-module/master/docs/img/arch_layer_approach.png "OCDM 3-layer architecture") 16 | 17 | Within the repository the browser glue code can be found at [src/browser/](../src/browser/). This glue code is used to talk to the component implementations of CDM and media engine. It dependends on the browser-specific EME implementation and might also be the responsible to link the CDM and media engine instance. A PPAPI specific glue implementation for Chromium can be found in the OCDM repositoy at [src/browser/chrome/](../src/browser/chrome/). This implements the Chrome-defined CDM interface and uses the OCDM core CDM component to forward the calls to a proper communication mechanism. 18 | Implementations for other browser environments just need to implement the EME-based interfaces according to their environment and use the core components accordingly. 19 | 20 | The core component implementations are located at [src/cdm/](../src/cdm/) resp. [src/mediaengine/](../src/mediaengine/). Both are implemented to mediate the calls from the browser layer via the communication channel to the DRM platform. If no modifications are necessary to be done at this stage of processing the components can be used and instantiated as is. If a DRM system needs specific modifications the core components can either be exchanged or extended according to the needs. 21 | 22 | The communication mechanisms are encapsulated by a communication interface and placed in [src/com/](../src/com/) for CDM [src/com/cdm/](../src/com/cdm/) and resp. [src/com/mediaengine/](../src/com/mediaengine/). The current OCDM implementation currently supports Unix RPC and shared memory. Other communication implementations can easily be added by using the communication interface without changes in the core layer and browser glue layer. 23 | 24 | ## CDM and CDMi 25 | 26 | Content Decryption Module interface (CDMi) is an open specification that encapsulates DRM platform specific calls with a common interface. As this is an open specified interface, CDM implementations can use it to communicate with any DRM system that exposes a CDMi. 27 | 28 | In addition to the OCDM repository we published a [OCDMi implementation](https://github.com/fraunhoferfokus/open-content-decryption-module-cdmi). The OCDMi can be used to implement an own CDMi-flavoured DRM platform service. It also has a mock implementation that works out of the box in combination with OCDM to demonstrate the usage and encourage own implemenations. 29 | 30 | ![CDM and CDMi](https://raw.githubusercontent.com/fraunhoferfokus/open-content-decryption-module/master/docs/img/arch_cdm_cdmi.png "CDM and CDMi") 31 | 32 | The OCDMi implementation is designed, similar to the OCDM, using a 3-layer approach but upside down. The communication layer holds CDMi core components that mediate calls to the DRM system glue code layer. For further details please refer to the [OCDMi repository](https://github.com/fraunhoferfokus/open-content-decryption-module-cdmi). 33 | 34 | As shown in the figure above the connection between OCDM and OCDMi works using the communication mechanism in both components. The RPC mechanism used in OCDM is compatible with the RPC interface used by OCDMi. To change the communication mechanism the only change that needs to be done is creating an addditional implementation in the communication layer for both components. -------------------------------------------------------------------------------- /src/browser/media_open_cdm.gypi: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2017 Fraunhofer FOKUS 3 | # 4 | # Licensed under the MIT License (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | # 13 | 14 | # Based on file contibuted to the Chromium project 15 | # media/media_cdm.gypi 16 | # License notice of original file: 17 | 18 | # Copyright 2013 The Chromium Authors. All rights reserved. 19 | # Use of this source code is governed by a BSD-style license that can be 20 | # found in the LICENSE file. 21 | 22 | { 23 | 'variables': { 24 | 'conditions': [ 25 | ['OS == "android"', { 26 | # Android doesn't use ffmpeg. 27 | 'use_ffmpeg%': 0, 28 | }, { # 'OS != "android"' 29 | 'use_ffmpeg%': 1, 30 | }], 31 | ], 32 | }, 33 | 'conditions': [ 34 | ['enable_pepper_cdms==1', { 35 | 'targets': [ 36 | { 37 | 'target_name': 'opencdm', 38 | 'type': 'none', 39 | 'conditions': [ 40 | ['use_ffmpeg == 1' , { 41 | 'defines': ['OCDM_USE_FFMPEG_DECODER', 'CLEAR_KEY_CDM_USE_FFMPEG_DECODER'], 42 | 'dependencies': [ 43 | '<(DEPTH)/third_party/ffmpeg/ffmpeg.gyp:ffmpeg', 44 | ], 45 | 'sources': [ 46 | '<(DEPTH)/media/cdm/ppapi/external_clear_key/ffmpeg_cdm_audio_decoder.cc', 47 | '<(DEPTH)/media/cdm/ppapi/external_clear_key/ffmpeg_cdm_audio_decoder.h', 48 | ], 49 | }], 50 | ['use_ffmpeg == 1' , { 51 | 'sources': [ 52 | '<(DEPTH)/media/cdm/ppapi/external_clear_key/ffmpeg_cdm_video_decoder.cc', 53 | '<(DEPTH)/media/cdm/ppapi/external_clear_key/ffmpeg_cdm_video_decoder.h', 54 | ], 55 | }], 56 | ['os_posix == 1 and OS != "mac" and enable_pepper_cdms==1', { 57 | 'type': 'loadable_module', # Must be in PRODUCT_DIR for ASAN bot. 58 | }], 59 | ['(OS == "mac" or OS == "win") and enable_pepper_cdms==1', { 60 | 'type': 'shared_library', 61 | }], 62 | ['OS == "mac"', { 63 | 'xcode_settings': { 64 | 'DYLIB_INSTALL_NAME_BASE': '@loader_path', 65 | }, 66 | }] 67 | ], 68 | 'defines': ['CDM_IMPLEMENTATION'], 69 | 'dependencies': [ 70 | 'media', 71 | '../url/url.gyp:url_lib', 72 | # Include the following for media::AudioBus. 73 | 'shared_memory_support', 74 | '<(DEPTH)/base/base.gyp:base', 75 | ], 76 | 'sources': [ 77 | '<(DEPTH)/media/cdm/ppapi/cdm_logging.cc', 78 | '<(DEPTH)/media/cdm/ppapi/cdm_logging.h', 79 | '../com/common/rpc/opencdm_xdr.h', 80 | '../com/common/rpc/opencdm_xdr_clnt.c', 81 | '../com/common/rpc/opencdm_xdr_xdr.c', 82 | '../com/common/rpc/opencdm_callback.h', 83 | '../com/common/rpc/opencdm_callback_xdr.c', 84 | '../cdm/open_cdm_platform_common.h', 85 | '../cdm/open_cdm_platform.h', 86 | '../cdm/open_cdm_platform_com.h', 87 | '../cdm/open_cdm_platform_factory.h', 88 | '../cdm/open_cdm_platform_com_callback_receiver.h', 89 | '../cdm/open_cdm_platform_impl.cc', 90 | '../cdm/open_cdm_platform_impl.h', 91 | '../com/cdm/open_cdm_platform_com_handler_factory.h', 92 | '../com/cdm/rpc/rpc_cdm_platform_handler.h', 93 | '../com/cdm/rpc/rpc_cdm_platform_handler.cc', 94 | '../com/common/shmemsem/shmemsem_helper.cc', 95 | '../com/common/shmemsem/shmemsem_helper.h', 96 | '../mediaengine/open_cdm_mediaengine.h', 97 | '../mediaengine/open_cdm_mediaengine_com.h', 98 | '../mediaengine/open_cdm_mediaengine_factory.h', 99 | '../mediaengine/open_cdm_mediaengine_impl.cc', 100 | '../mediaengine/open_cdm_mediaengine_impl.h', 101 | '../com/mediaengine/rpc/rpc_cdm_mediaengine_handler.cc', 102 | '../com/mediaengine/rpc/rpc_cdm_mediaengine_handler.h', 103 | '../browser/chrome/open_cdm.cc', 104 | '../browser/chrome/open_cdm.h', 105 | '../browser/chrome/open_cdm_chrome_common.h', 106 | '../common/open_cdm_common.h', 107 | '<(DEPTH)/media/cdm/ppapi/external_clear_key/cdm_video_decoder.cc', 108 | '<(DEPTH)/media/cdm/ppapi/external_clear_key/cdm_video_decoder.h', 109 | ], 110 | # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. 111 | 'msvs_disabled_warnings': [ 4267, ], 112 | }, 113 | { 114 | 'target_name': 'opencdmadapter', 115 | 'type': 'none', 116 | # Check whether the plugin's origin URL is valid. 117 | 'defines': ['CHECK_DOCUMENT_URL'], 118 | 'dependencies': [ 119 | '<(DEPTH)/ppapi/ppapi.gyp:ppapi_cpp', 120 | '<(DEPTH)/media/media_cdm_adapter.gyp:cdmadapter', 121 | 'opencdm', 122 | ], 123 | 'conditions': [ 124 | ['os_posix == 1 and OS != "mac" and enable_pepper_cdms==1', { 125 | # Because opencdm has type 'loadable_module' (see comments), 126 | # we must explicitly specify this dependency. 127 | 'libraries': [ 128 | # Built by opencdm. 129 | '<(PRODUCT_DIR)/libopencdm.so', 130 | ], 131 | }], 132 | ], 133 | }, 134 | ], 135 | }], 136 | ], 137 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Open Content Decryption Module 2 | 3 | The Open Content Decryption Module (OCDM) is a Content Decryption Module (CDM) according to W3C EME [1] specification to be used with HTML5 based browser environments and ecosystems [5]. The implementation enables DRM interoperability. 4 | OCDM is built with a modular concept in mind to be easily extendible and flexible. The open source code already provides support for the CDMi specification [4], which essentially requires the CDM to use the operating system's native RPC mechanisms to forward EME calls to the CDMi. 5 | Furthermore, the media sample transmission between a browser decoupled media engine and the CDMi can be secured via a DRM-specific authenticated interface. The CDMi itself contains most CDM logic and is a c++ wrapper for the embedded platform DRM. 6 | 7 | Fraunhofer FOKUS has developed the Open Content Decryption Module (OCDM) according to W3C EME specification to be used with HTML5 based browser environments. The implementation enables DRM interoperability. We would be happy to see a broad adoption of our implementation and encourage contributions. A first e2e implementation has been done testing with a Microsoft PlayReady CDMi implementation. 8 | 9 | ## Introduction / Purpose / Why this? 10 | 11 | * Interoperable HTML5 based protected video delivery 12 | * DRM interoperability 13 | * CENC, DASH 14 | * Plugin based integration (Pepper Plugin API) 15 | * CDMi allows open source browsers to support DRM without licensing it 16 | * e2e tested with Microsoft PlayReady DRM system 17 | 18 | ## Scope 19 | 20 | OCDM is developed according to W3C EME. This leads to a architecture as shown in the figure below, which consists of a Web application, a browser and a DRM platform layer. The browser as mediator between Web application and DRM platform exposes the EME and contains a CDM. The Web application is connected to the browser via EME. EME is mapped by the browser to a CDM and the CDM communicates to the DRM platform. 21 | 22 | As EME is modelling only the license retrieval and management, there is a second component that needs to talk to the DRM platform. In many cases, mainly in embedded environments, the media engine is separate from the browser and used by the browser as external renderer. In this case the media engine probably needs to authenticate itself at the DRM platform for handling and decoding of protected media data. 23 | 24 | ![Scope of OCDM](https://raw.githubusercontent.com/fraunhoferfokus/open-content-decryption-module/master/docs/img/ocdm_scope.png "Scope of OCDM") 25 | 26 | On the one hand, the OCDM's scope includes the generic CDM and its communication interfaces to the DRM platform. On the other hand, there are separate media engine communication interfaces to the DRM platform. OCDM contains sample implementations for the RPC communication mechanism, but is designed highly modular to be easily extended with other communication mechanisms. 27 | 28 | For detailed internals of the OCDM architecture please refer to the [architecture notes](./docs/architecture_notes_ocdm.md). 29 | 30 | ## References 31 | 32 | * [1] W3C Encrypted Media Extensions https://w3c.github.io/encrypted-media 33 | * [2] Interoperability Digital Rights Management and the Web https://scrivito-public.s3-eu-west-1.amazonaws.com/fokus/64257f12b90b464b/64a4d370cf46918585d0276caa858eae/FAME_Interoperability_Digital_Rights_Management_and_the_Web_flyer.pdf 34 | * [3] MSE-EME Reference Platform Documentation https://html5.cablelabs.com/mse-eme/doc/overview.html 35 | * [4] Content Decryption Module Interface Specification http://download.microsoft.com/download/E/A/4/EA470677-6C3C-4AFE-8A86-A196ADFD0F78/Content%20Decryption%20Module%20Interface%20Specification.pdf 36 | * [5] Fraunhofer FOKUS FAMIUM DRM http://www.fokus.fraunhofer.de/go/drm 37 | 38 | ## Supported Browsers and Platforms 39 | 40 | Currently OCDM development is compatible with following Web browers: 41 | 42 | * Chromium / CEF 43 | * Linux 44 | * Opera SDK 45 | * Linux 46 | 47 | For more details see the [milestones](https://github.com/fraunhoferfokus/open-content-decryption-module/milestones) page. 48 | 49 | ## How to build 50 | 51 | ### ...as Pepper Plugin for Chromium 52 | * clone this repository, e.g. into $HOME/opencdm 53 | * create the following symbolic link 54 | * ```$ ln -s $HOME/opencdm/src $CHROMIUM_ROOT/src/media/cdm/ppapi/external_open_cdm``` 55 | * add include into the ```$CHROMIUM_ROOT/src/media/media.gyp``` file to contain this: 56 | ``` 57 | 'includes': [ 58 | 'media_cdm.gypi', 59 | './cdm/ppapi/external_open_cdm/browser/media_open_cdm.gypi' 60 | ] 61 | ``` 62 | * apply changes by generating the project files 63 | * ```$ cd $CHROMIUM_ROOT/src``` 64 | * ```$ build/gyp_chromium``` 65 | * build the following target (please follow *[Build notes for Chromium](docs/build_notes_chromium.md)* section before building) 66 | * ```$ ninja -C out/Debug opencdmadapter``` 67 | 68 | ## How to run 69 | * integrate OCDM with your browser 70 | * example to be found in src/browser folder 71 | * setup communication to DRM system 72 | * sample code for this is provided in the separate [Open Content Decryption Module CDMi](https://github.com/fraunhoferfokus/open-content-decryption-module-cdmi) repository 73 | 74 | In practice the Pepper Plugin API based OCDM implementation can be launched as follows: 75 | 76 | ``` 77 | ./out/Debug/chrome --register-pepper-plugins="out/Debug/libopencdmadapter.so;application/x-ppapi-open-cdm" 78 | ``` 79 | 80 | ## Folder Structure 81 | 82 | Navigate the folders and see the readme files for further information. 83 | 84 | ## How to contribute 85 | 86 | See the [wiki](https://github.com/fraunhoferfokus/open-content-decryption-module/wiki) for information on how to contribute to this project. 87 | 88 | ## Known Issues / Comments 89 | 90 | This is a preliminary version of OCDM. Please file any issues or comments. 91 | 92 | * Chromium sandbox: Currently Chromium needs to be started with the --no-sandbox flag because of the current RPC mechanism. 93 | * Multiple session support is current work in progress. 94 | * Code needs more review from the community (e.g. memory allocation, appropriate data types). 95 | 96 | For more details see the [milestones](https://github.com/fraunhoferfokus/open-content-decryption-module/milestones) page. 97 | -------------------------------------------------------------------------------- /src/com/mediaengine/rpc/rpc_cdm_mediaengine_handler.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #include 15 | #include "media/cdm/ppapi/external_open_cdm/com/mediaengine/rpc/rpc_cdm_mediaengine_handler.h" 16 | #include "media/cdm/ppapi/cdm_logging.h" 17 | #include "media/cdm/ppapi/external_open_cdm/com/common/rpc/opencdm_xdr.h" 18 | 19 | namespace media { 20 | 21 | std::string rpcServer = "localhost"; 22 | // TODO(ska): outsource to config 23 | 24 | RpcCdmMediaengineHandler::RpcCdmMediaengineHandler(char *session_id_val, 25 | uint32_t session_id_len, 26 | uint8_t *auth_data_val, 27 | uint32_t auth_data_len) { 28 | CDM_DLOG() << "RpcCdmMediaengineHandler::RpcCdmMediaengineHandler"; 29 | 30 | sessionId.id = new char[session_id_len]; 31 | memcpy(sessionId.id, session_id_val, session_id_len); 32 | // TODO(ska): do we need this memcpy? 33 | sessionId.id = session_id_val; 34 | sessionId.idLen = session_id_len; 35 | 36 | if ((rpcClient = clnt_create(rpcServer.c_str(), OPEN_CDM, OPEN_CDM_EME_5, 37 | "tcp")) == NULL) { 38 | clnt_pcreateerror(rpcServer.c_str()); 39 | CDM_DLOG() << "rpcclient creation failed: " << rpcServer.c_str(); 40 | } else { 41 | CDM_DLOG() << "RpcCdmMediaengineHandler connected to server"; 42 | } 43 | 44 | rpc_response_generic *rpc_response; 45 | rpc_request_mediaengine_data rpc_param; 46 | rpc_param.session_id.session_id_val = session_id_val; 47 | rpc_param.session_id.session_id_len = session_id_len; 48 | rpc_param.auth_data.auth_data_val = auth_data_val; 49 | rpc_param.auth_data.auth_data_len = auth_data_len; 50 | 51 | // TODO(ska): need to check == 0? 52 | // if (idXchngShMem == 0) { 53 | idXchngShMem = AllocateSharedMemory(sizeof(shmem_info)); 54 | if (idXchngShMem < 0) { 55 | CDM_DLOG() << "idXchngShMem AllocateSharedMemory failed!"; 56 | } 57 | // } 58 | shMemInfo = reinterpret_cast(MapSharedMemory(idXchngShMem)); 59 | 60 | // SHARED MEMORY and SEMAPHORE INITIALIZATION 61 | shMemInfo->idSidShMem = 0; 62 | this->shMemInfo->idIvShMem = 0; 63 | this->shMemInfo->idSampleShMem = 0; 64 | this->shMemInfo->idSubsampleDataShMem = 0; 65 | this->shMemInfo->sidSize = 1; 66 | this->shMemInfo->ivSize = 2; 67 | this->shMemInfo->sampleSize = 3; 68 | this->shMemInfo->subsampleDataSize = 4; 69 | 70 | unsigned short vals[3]; 71 | vals[SEM_XCHNG_PUSH] = 1; 72 | vals[SEM_XCHNG_DECRYPT] = 0; 73 | vals[SEM_XCHNG_PULL] = 0; 74 | // if (idXchngSem == 0) { 75 | idXchngSem = CreateSemaphoreSet(3, vals); 76 | if (idXchngSem < 0) { 77 | CDM_DLOG() << "idXchngSem CreateSemaphoreSet failed!"; 78 | } 79 | // } 80 | 81 | rpc_param.id_exchange_shmem = idXchngShMem; 82 | rpc_param.id_exchange_sem = idXchngSem; 83 | 84 | if ((rpc_response = rpc_open_cdm_mediaengine_1(&rpc_param, rpcClient)) 85 | == NULL) { 86 | CDM_DLOG() << "engine session failed: " << rpcServer.c_str(); 87 | clnt_perror(rpcClient, rpcServer.c_str()); 88 | exit(5); 89 | } else { 90 | CDM_DLOG() << "engine session creation called"; 91 | } 92 | 93 | CDM_DLOG() << "create media engine session platform response: " 94 | << rpc_response->platform_val; 95 | } 96 | 97 | RpcCdmMediaengineHandler::~RpcCdmMediaengineHandler() { 98 | CDM_DLOG() << "RpcCdmMediaengineHandler destruct!"; 99 | // TODO(ska): is shared memory cleaned up correctly? 100 | // DeleteSemaphoreSet(idXchngSem); 101 | idXchngSem = 0; 102 | idXchngShMem = 0; 103 | } 104 | 105 | DecryptResponse RpcCdmMediaengineHandler::Decrypt(const uint8_t *pbIv, 106 | uint32_t cbIv, 107 | const uint8_t *pbData, 108 | uint32_t cbData, uint8_t *out, 109 | uint32_t &out_size) { 110 | printf("Decrypt-------\n"); 111 | CDM_DLOG() << "RpcCdmMediaengineHandler::Decrypt: "; 112 | DecryptResponse response; 113 | response.platform_response = PLATFORM_CALL_SUCCESS; 114 | response.sys_err = 0; 115 | // TODO(sph): real decryptresponse values need to 116 | // be written to sharedmem as well 117 | 118 | LockSemaphore(idXchngSem, SEM_XCHNG_PUSH); 119 | CDM_DLOG() << "LOCKed push lock"; 120 | 121 | cbIv = (cbIv != 8) ? 8 : cbIv; 122 | shMemInfo->idIvShMem = AllocateSharedMemory(cbIv); 123 | shMemInfo->ivSize = cbIv; 124 | 125 | uint8_t *pIvShMem = reinterpret_cast(MapSharedMemory( 126 | shMemInfo->idIvShMem)); 127 | memcpy(pIvShMem, pbIv, cbIv); 128 | // delete[] pbIv; 129 | 130 | shMemInfo->idSampleShMem = AllocateSharedMemory(cbData); 131 | shMemInfo->sampleSize = cbData; 132 | uint8_t *pSampleShMem = reinterpret_cast(MapSharedMemory( 133 | shMemInfo->idSampleShMem)); 134 | 135 | memcpy(pSampleShMem, pbData, cbData); 136 | // delete[] pbData; 137 | 138 | shMemInfo->idSubsampleDataShMem = 0; 139 | shMemInfo->subsampleDataSize = 0; 140 | CDM_DLOG() << "data ready to decrypt"; 141 | UnlockSemaphore(idXchngSem, SEM_XCHNG_DECRYPT); 142 | CDM_DLOG() << "WAIT for pull lock"; 143 | LockSemaphore(idXchngSem, SEM_XCHNG_PULL); 144 | CDM_DLOG() << "LOCKed pull lock"; 145 | // process clear data 146 | 147 | memcpy(out, pSampleShMem, cbData); 148 | out_size = cbData; 149 | 150 | CDM_DLOG() << "RUN fired!"; 151 | UnlockSemaphore(idXchngSem, SEM_XCHNG_PUSH); 152 | CDM_DLOG() << "UNLOCKed push lock"; 153 | 154 | // clean up current shared mems for sample data 155 | int err = DetachExistingSharedMemory(pIvShMem); 156 | CDM_DLOG() << "detached iv shmem " << shMemInfo->idIvShMem << ": " << err; 157 | err = DetachExistingSharedMemory(pSampleShMem); 158 | CDM_DLOG() << "detached sample shmem " << shMemInfo->idSampleShMem << ": " 159 | << err; 160 | 161 | return response; 162 | } 163 | 164 | 165 | } // namespace media 166 | -------------------------------------------------------------------------------- /src/com/common/rpc/opencdm_xdr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Please do not edit this file. 3 | * It was generated using rpcgen. 4 | */ 5 | 6 | #ifndef _OPENCDM_XDR_H_RPCGEN 7 | #define _OPENCDM_XDR_H_RPCGEN 8 | 9 | #include 10 | 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | 17 | struct rpc_request_is_type_supported { 18 | struct { 19 | u_int key_system_len; 20 | char *key_system_val; 21 | } key_system; 22 | struct { 23 | u_int mime_type_len; 24 | char *mime_type_val; 25 | } mime_type; 26 | }; 27 | typedef struct rpc_request_is_type_supported rpc_request_is_type_supported; 28 | 29 | struct rpc_request_mediakeys { 30 | struct { 31 | u_int key_system_len; 32 | char *key_system_val; 33 | } key_system; 34 | }; 35 | typedef struct rpc_request_mediakeys rpc_request_mediakeys; 36 | 37 | struct rpc_request_callback_info { 38 | struct { 39 | u_int hostname_len; 40 | char *hostname_val; 41 | } hostname; 42 | uint64_t prog_num; 43 | uint32_t prog_version; 44 | }; 45 | typedef struct rpc_request_callback_info rpc_request_callback_info; 46 | 47 | struct rpc_request_create_session { 48 | struct { 49 | u_int init_data_type_len; 50 | char *init_data_type_val; 51 | } init_data_type; 52 | struct { 53 | u_int init_data_len; 54 | uint8_t *init_data_val; 55 | } init_data; 56 | rpc_request_callback_info callback_info; 57 | }; 58 | typedef struct rpc_request_create_session rpc_request_create_session; 59 | 60 | struct rpc_request_load_session { 61 | struct { 62 | u_int session_id_len; 63 | char *session_id_val; 64 | } session_id; 65 | }; 66 | typedef struct rpc_request_load_session rpc_request_load_session; 67 | 68 | struct rpc_request_session_update { 69 | struct { 70 | u_int session_id_len; 71 | char *session_id_val; 72 | } session_id; 73 | struct { 74 | u_int key_len; 75 | uint8_t *key_val; 76 | } key; 77 | }; 78 | typedef struct rpc_request_session_update rpc_request_session_update; 79 | 80 | struct rpc_request_session_release { 81 | struct { 82 | u_int session_id_len; 83 | char *session_id_val; 84 | } session_id; 85 | }; 86 | typedef struct rpc_request_session_release rpc_request_session_release; 87 | 88 | struct rpc_request_mediaengine_data { 89 | struct { 90 | u_int session_id_len; 91 | char *session_id_val; 92 | } session_id; 93 | struct { 94 | u_int auth_data_len; 95 | uint8_t *auth_data_val; 96 | } auth_data; 97 | int32_t id_exchange_shmem; 98 | int32_t id_exchange_sem; 99 | }; 100 | typedef struct rpc_request_mediaengine_data rpc_request_mediaengine_data; 101 | 102 | struct rpc_response_generic { 103 | int platform_val; 104 | }; 105 | typedef struct rpc_response_generic rpc_response_generic; 106 | 107 | struct rpc_response_create_session { 108 | int platform_val; 109 | struct { 110 | u_int session_id_len; 111 | char *session_id_val; 112 | } session_id; 113 | }; 114 | typedef struct rpc_response_create_session rpc_response_create_session; 115 | 116 | #define OPEN_CDM 0x61135687 117 | #define OPEN_CDM_EME_5 1 118 | 119 | #if defined(__STDC__) || defined(__cplusplus) 120 | #define RPC_OPEN_CDM_IS_TYPE_SUPPORTED 1 121 | extern rpc_response_generic * rpc_open_cdm_is_type_supported_1(rpc_request_is_type_supported *, CLIENT *); 122 | extern rpc_response_generic * rpc_open_cdm_is_type_supported_1_svc(rpc_request_is_type_supported *, struct svc_req *); 123 | #define RPC_OPEN_CDM_MEDIAKEYS 2 124 | extern rpc_response_generic * rpc_open_cdm_mediakeys_1(rpc_request_mediakeys *, CLIENT *); 125 | extern rpc_response_generic * rpc_open_cdm_mediakeys_1_svc(rpc_request_mediakeys *, struct svc_req *); 126 | #define RPC_OPEN_CDM_MEDIAKEYS_CREATE_SESSION 3 127 | extern rpc_response_create_session * rpc_open_cdm_mediakeys_create_session_1(rpc_request_create_session *, CLIENT *); 128 | extern rpc_response_create_session * rpc_open_cdm_mediakeys_create_session_1_svc(rpc_request_create_session *, struct svc_req *); 129 | #define RPC_OPEN_CDM_MEDIAKEYS_LOAD_SESSION 4 130 | extern rpc_response_generic * rpc_open_cdm_mediakeys_load_session_1(rpc_request_load_session *, CLIENT *); 131 | extern rpc_response_generic * rpc_open_cdm_mediakeys_load_session_1_svc(rpc_request_load_session *, struct svc_req *); 132 | #define RPC_OPEN_CDM_MEDIAKEYSESSION_UPDATE 5 133 | extern rpc_response_generic * rpc_open_cdm_mediakeysession_update_1(rpc_request_session_update *, CLIENT *); 134 | extern rpc_response_generic * rpc_open_cdm_mediakeysession_update_1_svc(rpc_request_session_update *, struct svc_req *); 135 | #define RPC_OPEN_CDM_MEDIAKEYSESSION_RELEASE 6 136 | extern rpc_response_generic * rpc_open_cdm_mediakeysession_release_1(rpc_request_session_release *, CLIENT *); 137 | extern rpc_response_generic * rpc_open_cdm_mediakeysession_release_1_svc(rpc_request_session_release *, struct svc_req *); 138 | #define RPC_OPEN_CDM_MEDIAENGINE 7 139 | extern rpc_response_generic * rpc_open_cdm_mediaengine_1(rpc_request_mediaengine_data *, CLIENT *); 140 | extern rpc_response_generic * rpc_open_cdm_mediaengine_1_svc(rpc_request_mediaengine_data *, struct svc_req *); 141 | extern int open_cdm_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t); 142 | 143 | #else /* K&R C */ 144 | #define RPC_OPEN_CDM_IS_TYPE_SUPPORTED 1 145 | extern rpc_response_generic * rpc_open_cdm_is_type_supported_1(); 146 | extern rpc_response_generic * rpc_open_cdm_is_type_supported_1_svc(); 147 | #define RPC_OPEN_CDM_MEDIAKEYS 2 148 | extern rpc_response_generic * rpc_open_cdm_mediakeys_1(); 149 | extern rpc_response_generic * rpc_open_cdm_mediakeys_1_svc(); 150 | #define RPC_OPEN_CDM_MEDIAKEYS_CREATE_SESSION 3 151 | extern rpc_response_create_session * rpc_open_cdm_mediakeys_create_session_1(); 152 | extern rpc_response_create_session * rpc_open_cdm_mediakeys_create_session_1_svc(); 153 | #define RPC_OPEN_CDM_MEDIAKEYS_LOAD_SESSION 4 154 | extern rpc_response_generic * rpc_open_cdm_mediakeys_load_session_1(); 155 | extern rpc_response_generic * rpc_open_cdm_mediakeys_load_session_1_svc(); 156 | #define RPC_OPEN_CDM_MEDIAKEYSESSION_UPDATE 5 157 | extern rpc_response_generic * rpc_open_cdm_mediakeysession_update_1(); 158 | extern rpc_response_generic * rpc_open_cdm_mediakeysession_update_1_svc(); 159 | #define RPC_OPEN_CDM_MEDIAKEYSESSION_RELEASE 6 160 | extern rpc_response_generic * rpc_open_cdm_mediakeysession_release_1(); 161 | extern rpc_response_generic * rpc_open_cdm_mediakeysession_release_1_svc(); 162 | #define RPC_OPEN_CDM_MEDIAENGINE 7 163 | extern rpc_response_generic * rpc_open_cdm_mediaengine_1(); 164 | extern rpc_response_generic * rpc_open_cdm_mediaengine_1_svc(); 165 | extern int open_cdm_1_freeresult (); 166 | #endif /* K&R C */ 167 | 168 | /* the xdr functions */ 169 | 170 | #if defined(__STDC__) || defined(__cplusplus) 171 | extern bool_t xdr_rpc_request_is_type_supported (XDR *, rpc_request_is_type_supported*); 172 | extern bool_t xdr_rpc_request_mediakeys (XDR *, rpc_request_mediakeys*); 173 | extern bool_t xdr_rpc_request_callback_info (XDR *, rpc_request_callback_info*); 174 | extern bool_t xdr_rpc_request_create_session (XDR *, rpc_request_create_session*); 175 | extern bool_t xdr_rpc_request_load_session (XDR *, rpc_request_load_session*); 176 | extern bool_t xdr_rpc_request_session_update (XDR *, rpc_request_session_update*); 177 | extern bool_t xdr_rpc_request_session_release (XDR *, rpc_request_session_release*); 178 | extern bool_t xdr_rpc_request_mediaengine_data (XDR *, rpc_request_mediaengine_data*); 179 | extern bool_t xdr_rpc_response_generic (XDR *, rpc_response_generic*); 180 | extern bool_t xdr_rpc_response_create_session (XDR *, rpc_response_create_session*); 181 | 182 | #else /* K&R C */ 183 | extern bool_t xdr_rpc_request_is_type_supported (); 184 | extern bool_t xdr_rpc_request_mediakeys (); 185 | extern bool_t xdr_rpc_request_callback_info (); 186 | extern bool_t xdr_rpc_request_create_session (); 187 | extern bool_t xdr_rpc_request_load_session (); 188 | extern bool_t xdr_rpc_request_session_update (); 189 | extern bool_t xdr_rpc_request_session_release (); 190 | extern bool_t xdr_rpc_request_mediaengine_data (); 191 | extern bool_t xdr_rpc_response_generic (); 192 | extern bool_t xdr_rpc_response_create_session (); 193 | 194 | #endif /* K&R C */ 195 | 196 | #ifdef __cplusplus 197 | } 198 | #endif 199 | 200 | #endif /* !_OPENCDM_XDR_H_RPCGEN */ 201 | -------------------------------------------------------------------------------- /src/browser/chrome/open_cdm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | // Based on file contibuted to the Chromium project 15 | // media/cdm/ppapi/external_clear_key/clear_key_cdm.h 16 | // License notice of original file: 17 | 18 | // Copyright 2013 The Chromium Authors. All rights reserved. 19 | // Use of this source code is governed by a BSD-style license that can be 20 | // found in the LICENSE file. 21 | 22 | #ifndef MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_BROWSER_CHROME_OPEN_CDM_H_ 23 | #define MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_BROWSER_CHROME_OPEN_CDM_H_ 24 | 25 | #include 26 | #include 27 | #include "base/basictypes.h" // needed for media::MediaKeys::Exception 28 | #include "base/containers/scoped_ptr_hash_map.h" 29 | #include "base/memory/ref_counted.h" 30 | #include "base/memory/scoped_ptr.h" 31 | #include "base/synchronization/lock.h" 32 | #include "media/base/decryptor.h" 33 | #include "media/base/media_export.h" 34 | #include "media/base/media_keys.h" 35 | #include "media/base/decoder_buffer.h" 36 | 37 | #include "media/cdm/ppapi/external_open_cdm/browser/chrome/open_cdm_chrome_common.h" 38 | #include "media/cdm/ppapi/external_open_cdm/cdm/open_cdm_platform.h" 39 | #include "media/cdm/ppapi/external_open_cdm/cdm/open_cdm_platform_com_callback_receiver.h" 40 | #include "media/cdm/ppapi/external_open_cdm/mediaengine/open_cdm_mediaengine.h" 41 | 42 | #include "media/cdm/ppapi/external_clear_key/ffmpeg_cdm_audio_decoder.h" 43 | #include "media/cdm/ppapi/external_clear_key/ffmpeg_cdm_video_decoder.h" 44 | #include "media/cdm/ppapi/external_clear_key/cdm_video_decoder.h" 45 | 46 | #include "map" 47 | 48 | namespace media { 49 | 50 | class OpenCdm : public OpenCdmInterface, 51 | public OpenCdmPlatformComCallbackReceiver { 52 | public: 53 | OpenCdm(OpenCdmHost* host, const std::string& key_system); 54 | ~OpenCdm() override; 55 | 56 | // OpenDecryptor MediaKeys implementation 57 | void Initialize(bool allow_distinctive_identifier, 58 | bool allow_persistent_state) override; 59 | void CreateSessionAndGenerateRequest(uint32 promise_id, 60 | cdm::SessionType session_type, 61 | cdm::InitDataType init_data_type, 62 | const uint8* init_data, 63 | uint32 init_data_size) override; 64 | 65 | void LoadSession(uint32 promise_id, 66 | cdm::SessionType session_type, 67 | const char* web_session_id, 68 | uint32_t web_session_id_length) override; 69 | 70 | void CloseSession(uint32 promise_id, 71 | const char* web_session_id, 72 | uint32_t web_session_id_length) override; 73 | 74 | void UpdateSession(uint32 promise_id, 75 | const char* web_session_id, 76 | uint32_t web_session_id_length, 77 | const uint8* response, 78 | uint32 response_size) override; 79 | 80 | void RemoveSession(uint32 promise_id, 81 | const char* web_session_id, 82 | uint32_t web_session_id_length) override; 83 | 84 | void SetServerCertificate(uint32 promise_id, 85 | const uint8_t* server_certificate_data, 86 | uint32_t server_certificate_data_size) 87 | override; 88 | void TimerExpired(void* context) override; 89 | 90 | cdm::Status Decrypt(const cdm::InputBuffer& encrypted_buffer, 91 | cdm::DecryptedBlock* decrypted_block) override; 92 | cdm::Status InitializeAudioDecoder( 93 | const cdm::AudioDecoderConfig& audio_decoder_config) override; 94 | cdm::Status InitializeVideoDecoder( 95 | const cdm::VideoDecoderConfig& video_decoder_config) override; 96 | void DeinitializeDecoder(cdm::StreamType decoder_type) override; 97 | void ResetDecoder(cdm::StreamType decoder_type) override; 98 | cdm::Status DecryptAndDecodeFrame( 99 | const cdm::InputBuffer& encrypted_buffer, 100 | cdm::VideoFrame* me) 101 | override; 102 | cdm::Status DecryptAndDecodeSamples( 103 | const cdm::InputBuffer& encrypted_buffer, cdm::AudioFrames* audio_frames) 104 | override; 105 | void Destroy() override; 106 | void OnPlatformChallengeResponse( 107 | const cdm::PlatformChallengeResponse& response) override; 108 | 109 | void OnQueryOutputProtectionStatus( 110 | cdm::QueryResult result, 111 | uint32_t link_mask, 112 | uint32_t output_protection_mask) override; 113 | 114 | private: 115 | OpenCdmHost* host_; 116 | OpenCdmMediaengine *media_engine_; 117 | scoped_ptr platform_; 118 | std::map session_id_map; 119 | 120 | // Todo: remove this. It is from old CDM 121 | void ReadyCallback(OpenCdmPlatformSessionId platform_session_id) override; 122 | 123 | void ErrorCallback(OpenCdmPlatformSessionId platform_session_id, 124 | uint32_t sys_err, std::string err_msg) override; 125 | void MessageCallback(OpenCdmPlatformSessionId platform_session_id, 126 | std::string message, 127 | std::string destination_url) override; 128 | 129 | void OnKeyStatusUpdateCallback(OpenCdmPlatformSessionId platform_session_id, 130 | std::string message) override; 131 | 132 | // ContentDecryptionModule callbacks. 133 | void OnSessionMessage(const std::string& web_session_id, 134 | MediaKeys::MessageType message_type, 135 | const std::vector& message, 136 | const GURL& legacy_destination_url); 137 | 138 | void OnSessionKeysUpdate(const std::string& web_session_id, 139 | bool has_additional_usable_key, 140 | CdmKeysInfo keys_info); 141 | void OnSessionClosed(const std::string& web_session_id); 142 | 143 | // Handle the success/failure of a promise. These methods are responsible for 144 | // calling |host_| to resolve or reject the promise. 145 | void OnSessionCreated(uint32 promise_id, const std::string& web_session_id); 146 | void OnSessionLoaded(uint32 promise_id, const std::string& web_session_id); 147 | void OnPromiseResolved(uint32 promise_id); 148 | void OnPromiseFailed(uint32 promise_id, MediaKeys::Exception exception_code, 149 | uint32 system_code, const std::string& error_message); 150 | 151 | // Prepares next renewal message and sets a timer for it. 152 | void ScheduleNextRenewal(); 153 | 154 | 155 | // Prepares next heartbeat message and sets a timer for it. 156 | void ScheduleNextHeartBeat(); 157 | 158 | // Decrypts the |encrypted_buffer| and puts the result in |decrypted_buffer|. 159 | // Returns cdm::kSuccess if decryption succeeded. The decrypted result is 160 | // put in |decrypted_buffer|. If |encrypted_buffer| is empty, the 161 | // |decrypted_buffer| is set to an empty (EOS) buffer. 162 | // Returns cdm::kNoKey if no decryption key was available. In this case 163 | // |decrypted_buffer| should be ignored by the caller. 164 | // Returns cdm::kDecryptError if any decryption error occurred. In this case 165 | // |decrypted_buffer| should be ignored by the caller. 166 | cdm::Status DecryptToMediaDecoderBuffer( 167 | const cdm::InputBuffer& encrypted_buffer, 168 | scoped_refptr* decrypted_buffer); 169 | 170 | const std::string key_system_; 171 | std::string last_session_id_; 172 | cdm::StreamType last_stream_type_; 173 | cdm::Status audio_decoder_state_; 174 | cdm::Status video_decoder_state_; 175 | 176 | virtual OpenCdmPlatformSessionId GetPlatformSessionId( 177 | const std::string& web_session_id); 178 | virtual std::string GetChromeSessionId( 179 | OpenCdmPlatformSessionId platform_session_id); 180 | 181 | // Timer delay in milliseconds for the next host_->SetTimer() call. 182 | int64 timer_delay_ms_; 183 | 184 | // Indicates whether a renewal timer has been set to prevent multiple timers 185 | // from running. 186 | bool renewal_timer_set_; 187 | 188 | std::string next_renewal_message_; 189 | #if defined(OCDM_USE_FFMPEG_DECODER) 190 | scoped_ptr audio_decoder_; 191 | scoped_ptr video_decoder_; 192 | #endif // OPEN_CDM_USE_FFMPEG_DECODER 193 | }; 194 | 195 | } // namespace media 196 | 197 | #endif // MEDIA_CDM_PPAPI_EXTERNAL_OPEN_CDM_BROWSER_CHROME_OPEN_CDM_H_ 198 | -------------------------------------------------------------------------------- /src/browser/chrome/tests/ocdm_encrypted_media_istypesupported_browsertest.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | // Based on file contibuted to the Chromium project 15 | // chrome/browser/media/encrypted_media_istypesupported_browsertest.cc 16 | // License notice of original file: 17 | 18 | // Copyright 2013 The Chromium Authors. All rights reserved. 19 | // Use of this source code is governed by a BSD-style license that can be 20 | // found in the LICENSE file. 21 | 22 | #include 23 | #include 24 | 25 | #include "base/base_switches.h" 26 | #include "base/command_line.h" 27 | #include "base/files/file_path.h" 28 | #include "base/path_service.h" 29 | #include "base/strings/utf_string_conversions.h" 30 | #include "chrome/browser/ui/browser.h" 31 | #include "chrome/browser/ui/tabs/tab_strip_model.h" 32 | #include "chrome/common/chrome_paths.h" 33 | #include "chrome/test/base/in_process_browser_test.h" 34 | #include "chrome/test/base/ui_test_utils.h" 35 | #include "content/public/common/content_switches.h" 36 | #include "content/public/test/browser_test_utils.h" 37 | #include "url/gurl.h" 38 | 39 | #if defined(OS_ANDROID) 40 | #error This file needs to be updated to run on Android. 41 | #endif 42 | 43 | #if defined(USE_PROPRIETARY_CODECS) 44 | #define EXPECT_PROPRIETARY EXPECT_TRUE 45 | #else 46 | #define EXPECT_PROPRIETARY EXPECT_FALSE 47 | #endif 48 | 49 | // Expectations for OpenCDM. 50 | #define EXPECT_OCDM EXPECT_TRUE 51 | #define EXPECT_OCDMPROPRIETARY EXPECT_PROPRIETARY 52 | 53 | namespace chrome { 54 | 55 | #if defined(ENABLE_PEPPER_CDMS) 56 | 57 | const char kOpenCDM[] = "com.opencdm.mockdrm"; 58 | const char kOpenCDMParentOnly[] = "com.opencdm"; 59 | const char kOpenCDMParentOnlyPeriod[] = "com.opencdm."; 60 | const char kOpenCDMTldPeriod[] = "com."; 61 | const char kOpenCDMTld[] = "com"; 62 | const char kOpenCDMCaseSensitive[] = "com.opencdm.MoCkDrM"; 63 | const char kOpenCDMNonExisting[] = "com.opencdm.foo"; 64 | const char kOpenCDMChild[] = "com.opencdm.mockdrm.foo"; 65 | const char kOpenCDMExtraChar[] = "com.opencdm.mockdrmZ"; 66 | const char kOpenCDMLessChar[] = "com.opencdm.mockdr"; 67 | const char kPepperTypeForKeySystem[] = "application/x-ppapi-open-cdm"; 68 | const char kFilePathLiteral[] = "#OpenCDM#1.0.0.0;"; 69 | 70 | // TODO(xhwang): Simplify this test! See http://crbug.com/367158 71 | 72 | class OpenCDMEncryptedMediaIsTypeSupportedBaseTest : 73 | public InProcessBrowserTest { 74 | protected: 75 | OpenCDMEncryptedMediaIsTypeSupportedBaseTest() 76 | : is_test_page_loaded_(false), is_pepper_cdm_registered_(false) { 77 | vp8_codec_.push_back("vp8"); 78 | 79 | vp80_codec_.push_back("vp8.0"); 80 | 81 | vp9_codec_.push_back("vp9"); 82 | 83 | vp90_codec_.push_back("vp9.0"); 84 | 85 | vorbis_codec_.push_back("vorbis"); 86 | 87 | vp8_and_vorbis_codecs_.push_back("vp8"); 88 | vp8_and_vorbis_codecs_.push_back("vorbis"); 89 | 90 | vp9_and_vorbis_codecs_.push_back("vp9"); 91 | vp9_and_vorbis_codecs_.push_back("vorbis"); 92 | 93 | avc1_codec_.push_back("avc1"); 94 | 95 | avc1_extended_codec_.push_back("avc1.4D400C"); 96 | 97 | avc1_dot_codec_.push_back("avc1."); 98 | 99 | avc2_codec_.push_back("avc2"); 100 | 101 | avc3_codec_.push_back("avc3"); 102 | 103 | avc3_extended_codec_.push_back("avc3.64001f"); 104 | 105 | aac_codec_.push_back("mp4a"); 106 | 107 | avc1_and_aac_codecs_.push_back("avc1"); 108 | avc1_and_aac_codecs_.push_back("mp4a"); 109 | 110 | unknown_codec_.push_back("foo"); 111 | 112 | mixed_codecs_.push_back("vorbis"); 113 | mixed_codecs_.push_back("avc1"); 114 | } 115 | 116 | typedef std::vector CodecVector; 117 | 118 | const CodecVector& no_codecs() const { return no_codecs_; } 119 | const CodecVector& vp8_codec() const { return vp8_codec_; } 120 | const CodecVector& vp80_codec() const { return vp80_codec_; } 121 | const CodecVector& vp9_codec() const { return vp9_codec_; } 122 | const CodecVector& vp90_codec() const { return vp90_codec_; } 123 | const CodecVector& vorbis_codec() const { return vorbis_codec_; } 124 | const CodecVector& vp8_and_vorbis_codecs() const { 125 | return vp8_and_vorbis_codecs_; 126 | } 127 | const CodecVector& vp9_and_vorbis_codecs() const { 128 | return vp9_and_vorbis_codecs_; 129 | } 130 | const CodecVector& avc1_codec() const { return avc1_codec_; } 131 | const CodecVector& avc1_extended_codec() const { 132 | return avc1_extended_codec_; 133 | } 134 | const CodecVector& avc1_dot_codec() const { return avc1_dot_codec_; } 135 | const CodecVector& avc2_codec() const { return avc2_codec_; } 136 | const CodecVector& avc3_codec() const { return avc3_codec_; } 137 | const CodecVector& avc3_extended_codec() const { 138 | return avc3_extended_codec_; 139 | } 140 | const CodecVector& aac_codec() const { return aac_codec_; } 141 | const CodecVector& avc1_and_aac_codecs() const { 142 | return avc1_and_aac_codecs_; 143 | } 144 | const CodecVector& unknown_codec() const { return unknown_codec_; } 145 | const CodecVector& mixed_codecs() const { return mixed_codecs_; } 146 | 147 | // Update the command line to load |adapter_name| for 148 | // |pepper_type_for_key_system|. 149 | void RegisterPepperCdm(CommandLine* command_line, 150 | const std::string& adapter_name, 151 | const std::string& pepper_type_for_key_system, 152 | bool expect_adapter_exists = true) { 153 | DCHECK(!is_pepper_cdm_registered_) 154 | << "RegisterPepperCdm() can only be called once."; 155 | is_pepper_cdm_registered_ = true; 156 | 157 | // Append the switch to register the appropriate adapter. 158 | base::FilePath plugin_dir; 159 | EXPECT_TRUE(PathService::Get(base::DIR_MODULE, &plugin_dir)); 160 | base::FilePath plugin_lib = plugin_dir.AppendASCII(adapter_name); 161 | EXPECT_EQ(expect_adapter_exists, base::PathExists(plugin_lib)); 162 | base::FilePath::StringType pepper_plugin = plugin_lib.value(); 163 | pepper_plugin.append(FILE_PATH_LITERAL(kFilePathLiteral)); 164 | #if defined(OS_WIN) 165 | pepper_plugin.append(base::ASCIIToWide(pepper_type_for_key_system)); 166 | #else 167 | pepper_plugin.append(pepper_type_for_key_system); 168 | #endif 169 | command_line->AppendSwitchNative(switches::kRegisterPepperPlugins, 170 | pepper_plugin); 171 | // TODO(mla): remove after communication to cdmi is possible 172 | // from within the sandbox 173 | command_line->AppendSwitch(switches::kNoSandbox); 174 | // TODO(mla): swich needed until unprefixed EME version available 175 | command_line->AppendSwitch(switches::kEnableEncryptedMedia); 176 | } 177 | 178 | void LoadTestPage() { 179 | // Load the test page needed. IsConcreteSupportedKeySystem() needs some 180 | // JavaScript and a video loaded in order to work. 181 | if (!is_test_page_loaded_) { 182 | ASSERT_TRUE(test_server()->Start()); 183 | GURL gurl = test_server()->GetURL( 184 | "files/media/drmock/test.html"); 185 | ui_test_utils::NavigateToURL(browser(), gurl); 186 | is_test_page_loaded_ = true; 187 | } 188 | } 189 | 190 | bool IsConcreteSupportedKeySystem(const std::string& key) { 191 | std::string command( 192 | "window.domAutomationController.send(MediaKeys_isTypeSupported('"); 193 | command.append(key); 194 | command.append("'));"); 195 | 196 | // testKeySystemInstantiation() is a JavaScript function which needs to 197 | // be loaded. 198 | LoadTestPage(); 199 | 200 | std::string result; 201 | EXPECT_TRUE(content::ExecuteScriptAndExtractString( 202 | browser()->tab_strip_model()->GetActiveWebContents(), 203 | command, 204 | &result)); 205 | CHECK(result == "success" || result == "NotSupportedError") << result; 206 | return (result == "success"); 207 | } 208 | 209 | bool IsSupportedKeySystemWithMediaMimeType(const std::string& type, 210 | const CodecVector& codecs, 211 | const std::string& keySystem) { 212 | std::string command("document.createElement('video').canPlayType("); 213 | if (type.empty()) { 214 | // Simple case, pass "null" as first argument. 215 | command.append("null"); 216 | DCHECK(codecs.empty()); 217 | } else { 218 | command.append("'"); 219 | command.append(type); 220 | if (!codecs.empty()) { 221 | command.append("; codecs=\""); 222 | for (CodecVector::const_iterator it = codecs.begin(); 223 | it != codecs.end(); 224 | ++it) { 225 | command.append(*it); 226 | command.append(","); 227 | } 228 | command.replace(command.length() - 1, 1, "\""); 229 | } 230 | command.append("'"); 231 | } 232 | command.append(",'"); 233 | command.append(keySystem); 234 | command.append("')"); 235 | 236 | std::string result; 237 | EXPECT_TRUE(content::ExecuteScriptAndExtractString( 238 | browser()->tab_strip_model()->GetActiveWebContents(), 239 | "window.domAutomationController.send(" + command + ");", 240 | &result)); 241 | return (result == "maybe" || result == "probably"); 242 | } 243 | 244 | private: 245 | const CodecVector no_codecs_; 246 | CodecVector vp8_codec_; 247 | CodecVector vp80_codec_; 248 | CodecVector vp9_codec_; 249 | CodecVector vp90_codec_; 250 | CodecVector vorbis_codec_; 251 | CodecVector vp8_and_vorbis_codecs_; 252 | CodecVector vp9_and_vorbis_codecs_; 253 | CodecVector avc1_codec_; 254 | CodecVector avc1_extended_codec_; 255 | CodecVector avc1_dot_codec_; 256 | CodecVector avc2_codec_; 257 | CodecVector avc3_codec_; 258 | CodecVector avc3_extended_codec_; 259 | CodecVector aac_codec_; 260 | CodecVector avc1_and_aac_codecs_; 261 | CodecVector unknown_codec_; 262 | CodecVector mixed_codecs_; 263 | bool is_test_page_loaded_; 264 | bool is_pepper_cdm_registered_; 265 | }; 266 | 267 | // For PPAPI OpenCDM tests, ensure that the OpenCDM adapter is loaded. 268 | class OpenCDMEncryptedMediaIsTypeSupportedTest 269 | : public OpenCDMEncryptedMediaIsTypeSupportedBaseTest { 270 | 271 | protected: 272 | virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 273 | // Platform-specific filename relative to the chrome executable. 274 | const char adapter_file_name[] = 275 | #if defined(OS_MACOSX) 276 | "opencdmadapter.plugin"; 277 | #elif defined(OS_WIN) 278 | "opencdmadapter.dll"; 279 | #elif defined(OS_POSIX) 280 | "libopencdmadapter.so"; 281 | #endif 282 | 283 | const std::string pepper_name(kPepperTypeForKeySystem); 284 | RegisterPepperCdm(command_line, adapter_file_name, pepper_name); 285 | } 286 | 287 | }; 288 | 289 | // Registers OpenCDM with the wrong path (filename). 290 | class OpenCDMEncryptedMediaIsTypeSupportedRegisteredWithWrongPathTest 291 | : public OpenCDMEncryptedMediaIsTypeSupportedBaseTest { 292 | protected: 293 | virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 294 | RegisterPepperCdm(command_line, 295 | "libopencdmadapterwrongname.so", 296 | kPepperTypeForKeySystem, 297 | false); 298 | } 299 | }; 300 | 301 | // 302 | // OpenCDM Tests 303 | // 304 | 305 | // When defined(ENABLE_PEPPER_CDMS), this also tests the Pepper CDM check. 306 | IN_PROC_BROWSER_TEST_F(OpenCDMEncryptedMediaIsTypeSupportedTest, 307 | OpenCDM_Basic) { 308 | EXPECT_OCDM(IsConcreteSupportedKeySystem(kOpenCDM)); 309 | EXPECT_OCDM(IsSupportedKeySystemWithMediaMimeType( 310 | "video/mp4", no_codecs(), kOpenCDM)); 311 | } 312 | 313 | IN_PROC_BROWSER_TEST_F(OpenCDMEncryptedMediaIsTypeSupportedTest, 314 | OpenCDM_Parent) { 315 | // The parent should be supported but is not. See http://crbug.com/164303. 316 | EXPECT_FALSE(IsConcreteSupportedKeySystem(kOpenCDMParentOnly)); 317 | EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( 318 | "video/mp4", no_codecs(), kOpenCDMParentOnly)); 319 | } 320 | 321 | IN_PROC_BROWSER_TEST_F(OpenCDMEncryptedMediaIsTypeSupportedTest, 322 | OpenCDM_IsSupportedKeySystem_InvalidVariants) { 323 | // Case sensitive. 324 | EXPECT_FALSE(IsConcreteSupportedKeySystem(kOpenCDMCaseSensitive)); 325 | // This should fail, but currently canPlayType() converts it to lowercase. 326 | // See http://crbug.com/286036. 327 | EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType( 328 | "video/mp4", no_codecs(), kOpenCDMCaseSensitive)); 329 | 330 | // TLDs are not allowed. 331 | EXPECT_FALSE(IsConcreteSupportedKeySystem(kOpenCDMTldPeriod)); 332 | EXPECT_FALSE( 333 | IsSupportedKeySystemWithMediaMimeType("video/mp4", no_codecs(), kOpenCDMTldPeriod)); 334 | EXPECT_FALSE(IsConcreteSupportedKeySystem(kOpenCDMTld)); 335 | EXPECT_FALSE( 336 | IsSupportedKeySystemWithMediaMimeType("video/mp4", no_codecs(), kOpenCDMTld)); 337 | 338 | // Extra period. 339 | EXPECT_FALSE(IsConcreteSupportedKeySystem(kOpenCDMParentOnlyPeriod)); 340 | EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( 341 | "video/mp4", no_codecs(), kOpenCDMParentOnlyPeriod)); 342 | 343 | // Incomplete. 344 | EXPECT_FALSE(IsConcreteSupportedKeySystem(kOpenCDMLessChar)); 345 | EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( 346 | "video/mp4", no_codecs(), kOpenCDMLessChar)); 347 | 348 | // Extra character. 349 | EXPECT_FALSE(IsConcreteSupportedKeySystem(kOpenCDMExtraChar)); 350 | EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( 351 | "video/mp4", no_codecs(), kOpenCDMExtraChar)); 352 | 353 | // There are no child key systems for OpenCDM. 354 | EXPECT_FALSE( 355 | IsConcreteSupportedKeySystem(kOpenCDMChild)); 356 | EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( 357 | "video/mp4", no_codecs(), kOpenCDMChild)); 358 | } 359 | 360 | IN_PROC_BROWSER_TEST_F( 361 | OpenCDMEncryptedMediaIsTypeSupportedTest, 362 | IsSupportedKeySystemWithMediaMimeType_OpenCDM_NoType) { 363 | // These two should be true. See http://crbug.com/164303. 364 | EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( 365 | std::string(), no_codecs(), kOpenCDM)); 366 | EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( 367 | std::string(), no_codecs(), kOpenCDMParentOnly)); 368 | 369 | EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( 370 | std::string(), no_codecs(), kOpenCDMNonExisting)); 371 | EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( 372 | std::string(), no_codecs(), kOpenCDMChild)); 373 | } 374 | 375 | IN_PROC_BROWSER_TEST_F( 376 | OpenCDMEncryptedMediaIsTypeSupportedTest, 377 | IsSupportedKeySystemWithMediaMimeType_OpenCDM_MP4) { 378 | // Valid video types. 379 | EXPECT_OCDMPROPRIETARY(IsSupportedKeySystemWithMediaMimeType( 380 | "video/mp4", no_codecs(), kOpenCDM)); 381 | // The parent should be supported but is not. See http://crbug.com/164303. 382 | EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( 383 | "video/mp4", no_codecs(), kOpenCDMParentOnly)); 384 | EXPECT_OCDMPROPRIETARY(IsSupportedKeySystemWithMediaMimeType( 385 | "video/mp4", avc1_codec(), kOpenCDM)); 386 | EXPECT_OCDMPROPRIETARY(IsSupportedKeySystemWithMediaMimeType( 387 | "video/mp4", avc1_and_aac_codecs(), kOpenCDM)); 388 | EXPECT_OCDMPROPRIETARY(IsSupportedKeySystemWithMediaMimeType( 389 | "video/mp4", avc3_codec(), kOpenCDM)); 390 | EXPECT_OCDMPROPRIETARY(IsSupportedKeySystemWithMediaMimeType( 391 | "video/mp4", aac_codec(), kOpenCDM)); 392 | 393 | // Extended codecs. 394 | EXPECT_OCDMPROPRIETARY(IsSupportedKeySystemWithMediaMimeType( 395 | "video/mp4", avc1_extended_codec(), kOpenCDM)); 396 | EXPECT_OCDMPROPRIETARY(IsSupportedKeySystemWithMediaMimeType( 397 | "video/mp4", avc3_extended_codec(), kOpenCDM)); 398 | 399 | // Invalid codec format, but canPlayType() strips away the period. 400 | EXPECT_OCDMPROPRIETARY(IsSupportedKeySystemWithMediaMimeType( 401 | "video/mp4", avc1_dot_codec(), kOpenCDM)); 402 | 403 | // Non-MP4 codecs. 404 | EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( 405 | "video/mp4", avc2_codec(), kOpenCDM)); 406 | EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( 407 | "video/mp4", vp8_codec(), kOpenCDM)); 408 | EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( 409 | "video/mp4", unknown_codec(), kOpenCDM)); 410 | EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( 411 | "video/mp4", mixed_codecs(), kOpenCDM)); 412 | 413 | // Valid audio types. 414 | EXPECT_OCDMPROPRIETARY(IsSupportedKeySystemWithMediaMimeType( 415 | "audio/mp4", no_codecs(), kOpenCDM)); 416 | EXPECT_OCDMPROPRIETARY(IsSupportedKeySystemWithMediaMimeType( 417 | "audio/mp4", aac_codec(), kOpenCDM)); 418 | 419 | // Non-audio codecs. 420 | EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( 421 | "audio/mp4", avc1_codec(), kOpenCDM)); 422 | EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( 423 | "audio/mp4", avc1_and_aac_codecs(), kOpenCDM)); 424 | EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( 425 | "audio/mp4", avc3_codec(), kOpenCDM)); 426 | 427 | // Non-MP4 codec. 428 | EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( 429 | "audio/mp4", vorbis_codec(), kOpenCDM)); 430 | } 431 | 432 | 433 | // Since this test fixture does not register the CDMs on the command line, the 434 | // check for the CDMs in chrome_key_systems.cc should fail, and they should not 435 | // be registered with KeySystems. 436 | IN_PROC_BROWSER_TEST_F(OpenCDMEncryptedMediaIsTypeSupportedBaseTest, 437 | PepperCDMsNotRegistered) { 438 | EXPECT_FALSE(IsConcreteSupportedKeySystem(kOpenCDM)); 439 | EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( 440 | "video/mp4", no_codecs(), kOpenCDM)); 441 | } 442 | 443 | // Since this test fixture does not register the CDMs on the command line, the 444 | // check for the CDMs in chrome_key_systems.cc should fail, and they should not 445 | // be registered with KeySystems. 446 | IN_PROC_BROWSER_TEST_F( 447 | OpenCDMEncryptedMediaIsTypeSupportedRegisteredWithWrongPathTest, 448 | PepperCDMsRegisteredButAdapterNotPresent) { 449 | EXPECT_FALSE(IsConcreteSupportedKeySystem(kOpenCDM)); 450 | EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType( 451 | "video/mp4", no_codecs(), kOpenCDM)); 452 | } 453 | 454 | #endif // defined(ENABLE_PEPPER_CDMS) 455 | 456 | } // namespace chrome 457 | -------------------------------------------------------------------------------- /src/com/cdm/rpc/rpc_cdm_platform_handler.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include "media/cdm/ppapi/external_open_cdm/com/cdm/rpc/rpc_cdm_platform_handler.h" 18 | #include "media/cdm/ppapi/cdm_logging.h" 19 | 20 | extern "C" { 21 | #include "media/cdm/ppapi/external_open_cdm/com/common/rpc/opencdm_xdr.h" 22 | } 23 | 24 | typedef struct { 25 | int prognum; 26 | int sock; 27 | } thread_parm_t; 28 | // TODO(ska): rename thread_parm_t 29 | 30 | 31 | namespace media { 32 | 33 | unsigned long gettransient(int proto, int vers, int *sockp) { 34 | static int prognum = 0x40000000; 35 | int s, len, socktype; 36 | struct sockaddr_in addr; 37 | switch (proto) { 38 | case IPPROTO_UDP: 39 | socktype = SOCK_DGRAM; 40 | break; 41 | case IPPROTO_TCP: 42 | socktype = SOCK_STREAM; 43 | break; 44 | default: 45 | CDM_DLOG() << "unknown protocol type"; 46 | return 0; 47 | } 48 | if (*sockp == RPC_ANYSOCK) { 49 | if ((s = socket(AF_INET, socktype, 0)) < 0) { 50 | perror("socket"); 51 | return (0); 52 | } 53 | *sockp = s; 54 | } else { 55 | s = *sockp; 56 | } 57 | addr.sin_addr.s_addr = 0; 58 | addr.sin_family = AF_INET; 59 | addr.sin_port = 0; 60 | len = sizeof(addr); 61 | // may be already bound, so don't check for error 62 | bind(s, (const sockaddr*) &addr, len); 63 | if (getsockname(s, reinterpret_cast(&addr), 64 | reinterpret_cast(&len)) < 0) { 65 | perror("getsockname"); 66 | return (0); 67 | } 68 | while (!pmap_set(prognum++, vers, proto, ntohs(addr.sin_port))) 69 | continue; 70 | return (prognum - 1); 71 | } 72 | 73 | static std::map rpc_cdm_platform_con_map; 74 | 75 | struct RpcThreadCallParam { 76 | RpcCdmPlatformHandler *caller; 77 | thread_parm_t *thread_parm; 78 | }; 79 | 80 | RpcCdmPlatformHandler::RpcCdmPlatformHandler( 81 | OpenCdmPlatformComCallbackReceiver *callback_receiver) 82 | : callback_receiver_(callback_receiver) { 83 | CDM_DLOG() << "new RpcCdmPlatformHandler instance"; 84 | 85 | com_state = UNINITIALIZED; 86 | 87 | rpc_server_host = "localhost"; 88 | 89 | // prepare starting threaded RPC Server 90 | pthread_t thread1; 91 | thread_parm_t *parm = NULL; 92 | int rc = 0; 93 | 94 | int sock = RPC_ANYSOCK; 95 | int prognum = gettransient(IPPROTO_TCP, 1, &sock); 96 | rpc_prog = prognum; 97 | rpc_client = NULL; 98 | 99 | if (prognum == 0) { 100 | com_state = FAULTY; 101 | CDM_DLOG() << "new RpcCdmPlatformHandler instance creation failed."; 102 | return; 103 | } 104 | rpc_cdm_platform_con_map[prognum] = this; 105 | 106 | /* set up multiple parameters to pass to the thread */ 107 | parm = reinterpret_cast(malloc(sizeof(thread_parm_t))); 108 | parm->sock = sock; 109 | parm->prognum = prognum; 110 | RpcThreadCallParam *call_params = new RpcThreadCallParam(); 111 | call_params->caller = this; 112 | call_params->thread_parm = parm; 113 | rc = pthread_create(&thread1, NULL, RpcCdmPlatformHandler::DelegateRpcInit, 114 | call_params); 115 | CDM_DLOG() << "thread created: " << rc; 116 | 117 | com_state = INITIALIZED; 118 | 119 | // TODO(sph): pthread_exit to terminate thread 120 | } 121 | 122 | void *RpcCdmPlatformHandler::DelegateRpcInit(void *call_params) { 123 | RpcThreadCallParam *call_param = 124 | reinterpret_cast(call_params); 125 | // delegate call to caller instance 126 | return call_param->caller->RpcInitPrivate(call_param->thread_parm); 127 | } 128 | 129 | 130 | void RpcCdmPlatformHandler::OnMessage1SvcDelegate(rpc_cb_message *kmm, struct svc_req *rqstp, RpcCdmPlatformHandler *p_instance) 131 | { 132 | CDM_DLOG() << "on_key_message_1_svc"; 133 | p_instance->OnMessage1Svc(kmm, rqstp); 134 | } 135 | 136 | void RpcCdmPlatformHandler::OnKeyStatusUpdate1SvcDelegate( 137 | rpc_cb_key_status_update *kmm, struct svc_req *rqstp, 138 | RpcCdmPlatformHandler *p_instance) 139 | { 140 | CDM_DLOG() << "on_key_status_update_1_svc"; 141 | OpenCdmPlatformSessionId session_id; 142 | std::string message = std::string(kmm->message); 143 | 144 | session_id.session_id_len = kmm->session_id.session_id_len; 145 | session_id.session_id = kmm->session_id.session_id_val; 146 | 147 | p_instance->callback_receiver_->OnKeyStatusUpdateCallback(session_id, message); 148 | } 149 | 150 | 151 | void RpcCdmPlatformHandler::OnMessage1Svc(rpc_cb_message *kmm, struct svc_req *) 152 | { 153 | CDM_DLOG() << "on_key_message_1_svc"; 154 | CDM_DLOG() << "len: " << kmm->session_id.session_id_len; 155 | CDM_DLOG() << "val lic. " << kmm->destination_url; 156 | 157 | std::string s; 158 | std::string delimiter = "#SPLIT#"; 159 | std::string laURL; 160 | std::string message; 161 | OpenCdmPlatformSessionId session_id; 162 | 163 | session_id.session_id_len = kmm->session_id.session_id_len; 164 | session_id.session_id = kmm->session_id.session_id_val; 165 | 166 | s = kmm->destination_url; 167 | laURL = s.substr(0, s.find(delimiter)); 168 | message = s.substr(s.find(delimiter) + delimiter.size(), s.size()); 169 | CDM_DLOG() << "LA_URL: " << laURL.c_str(); 170 | CDM_DLOG() << "KEY_MESSAGE received: " << message.c_str(); 171 | 172 | //get open_media_keys instance to execute callbacks 173 | this->callback_receiver_->MessageCallback(session_id, message, laURL); 174 | } 175 | 176 | void RpcCdmPlatformHandler::OnReady1SvcDelegate(rpc_cb_ready *keyready_param, struct svc_req *rqstp, RpcCdmPlatformHandler *p_instance) 177 | { 178 | CDM_DLOG() << "on_key_ready_1_svc"; 179 | p_instance->OnReady1Svc(keyready_param, rqstp); 180 | } 181 | 182 | void RpcCdmPlatformHandler::OnReady1Svc(rpc_cb_ready *kr, struct svc_req *) 183 | { 184 | CDM_DLOG() << "on_key_ready_1_svc"; 185 | OpenCdmPlatformSessionId session_id; 186 | 187 | session_id.session_id_len = kr->session_id.session_id_len; 188 | session_id.session_id = kr->session_id.session_id_val; 189 | this->callback_receiver_->ReadyCallback(session_id); 190 | 191 | } 192 | 193 | void RpcCdmPlatformHandler::OnError1SvcDelegate(rpc_cb_error *err_param, struct svc_req *rqstp, RpcCdmPlatformHandler *p_instance) 194 | { 195 | CDM_DLOG() << "on_key_error_1_svc"; 196 | p_instance->OnError1Svc(err_param, rqstp); 197 | } 198 | 199 | void RpcCdmPlatformHandler::OnError1Svc(rpc_cb_error * ke, struct svc_req *) 200 | { 201 | CDM_DLOG() << "on_key_error_1_svc"; 202 | OpenCdmPlatformSessionId session_id; 203 | 204 | session_id.session_id_len = ke->session_id.session_id_len; 205 | session_id.session_id = ke->session_id.session_id_val; 206 | int sys_error = 0; 207 | // TODO (sph): set real error message if there is any 208 | this->callback_receiver_->ErrorCallback(session_id, sys_error,"KEY_ERROR"); 209 | } 210 | 211 | //static void RpcCdmPlatformHandler::open_cdm_callback_1(struct svc_req *rqstp, register SVCXPRT *transp) 212 | void RpcCdmPlatformHandler::RpcCallbackPrivate(struct svc_req *rqstp, register SVCXPRT *transp) 213 | { 214 | union { 215 | rpc_cb_message on_message_1_arg; 216 | rpc_cb_ready on_ready_1_arg; 217 | rpc_cb_error on_error_1_arg; 218 | } argument; 219 | char *result; 220 | xdrproc_t _xdr_argument, _xdr_result; 221 | char *(*local)(char *, struct svc_req *, RpcCdmPlatformHandler *); 222 | 223 | switch (rqstp->rq_proc) { 224 | case NULLPROC: 225 | (void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *)NULL); 226 | return; 227 | 228 | case ON_KEY_MESSAGE: 229 | _xdr_argument = (xdrproc_t) xdr_rpc_cb_message; 230 | _xdr_result = (xdrproc_t) xdr_void; 231 | local = (char *(*)(char *, struct svc_req *, RpcCdmPlatformHandler *)) RpcCdmPlatformHandler::OnMessage1SvcDelegate; 232 | break; 233 | 234 | case ON_KEY_READY: 235 | _xdr_argument = (xdrproc_t) xdr_rpc_cb_ready; 236 | _xdr_result = (xdrproc_t) xdr_void; 237 | local = (char *(*)(char *, struct svc_req *, RpcCdmPlatformHandler *)) RpcCdmPlatformHandler::OnReady1SvcDelegate; 238 | break; 239 | 240 | case ON_KEY_ERROR: 241 | _xdr_argument = (xdrproc_t) xdr_rpc_cb_error; 242 | _xdr_result = (xdrproc_t) xdr_void; 243 | local = (char *(*)(char *, struct svc_req *, RpcCdmPlatformHandler *)) RpcCdmPlatformHandler::OnError1SvcDelegate; 244 | break; 245 | 246 | case ON_KEY_STATUS_UPDATE: 247 | _xdr_argument = (xdrproc_t) xdr_rpc_cb_key_status_update; 248 | _xdr_result = (xdrproc_t) xdr_void; 249 | local = (char *(*)(char *, struct svc_req *, RpcCdmPlatformHandler *)) 250 | RpcCdmPlatformHandler::OnKeyStatusUpdate1SvcDelegate; 251 | break; 252 | 253 | default: 254 | svcerr_noproc (transp); 255 | return; 256 | } 257 | memset ((char *)&argument, 0, sizeof (argument)); 258 | if (!svc_getargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) { 259 | svcerr_decode (transp); 260 | return; 261 | } 262 | result = (*local)((char *)&argument, rqstp, this); 263 | if (result != NULL && !svc_sendreply(transp, (xdrproc_t) _xdr_result, result)) { 264 | svcerr_systemerr (transp); 265 | } 266 | if (!svc_freeargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) { 267 | fprintf (stderr, "%s", "unable to free arguments"); 268 | exit (1); 269 | } 270 | return; 271 | } 272 | 273 | 274 | void *RpcCdmPlatformHandler::RpcInitPrivate(void *thread_parm) { 275 | // async callback stuff 276 | 277 | SVCXPRT *xprt; 278 | thread_parm_t *p = reinterpret_cast(thread_parm); 279 | int sock = p->sock; 280 | int prognum = p->prognum; 281 | 282 | if ((xprt = svctcp_create(sock, 0, 0)) == NULL) { 283 | return NULL; 284 | } 285 | 286 | // protocol is 0 - gettransient does registering 287 | (void) svc_register(xprt, prognum, 1, 288 | RpcCdmPlatformHandler::DelegateRpcCallback, IPPROTO_TCP); 289 | 290 | rpc_prog = prognum; 291 | 292 | svc_run(); 293 | CDM_DLOG() << "svc_run executed"; 294 | free(p); 295 | return NULL; 296 | } 297 | 298 | void RpcCdmPlatformHandler::DelegateRpcCallback(struct svc_req *rqstp, 299 | register SVCXPRT *transp) { 300 | if (rpc_cdm_platform_con_map.find(rqstp->rq_prog) 301 | != rpc_cdm_platform_con_map.end()) { 302 | RpcCdmPlatformHandler *caller = rpc_cdm_platform_con_map[rqstp->rq_prog]; 303 | caller->RpcCallbackPrivate(rqstp, transp); 304 | } 305 | } 306 | 307 | MediaKeysResponse RpcCdmPlatformHandler::MediaKeys(std::string key_system) { 308 | CDM_DLOG() << "RpcCdmPlatformHandler::MediaKeys"; 309 | MediaKeysResponse response; 310 | 311 | // rpc not ready 312 | if (com_state == FAULTY) { 313 | response.platform_response = PLATFORM_CALL_FAIL; 314 | CDM_DLOG() << "RpcCdmPlatformHandler::MediaKeys connection state faulty"; 315 | return response; 316 | } 317 | 318 | if ((rpc_client = clnt_create(rpc_server_host.c_str(), OPEN_CDM, 319 | OPEN_CDM_EME_5, 320 | "tcp")) == NULL) { 321 | com_state = FAULTY; 322 | clnt_pcreateerror(rpc_server_host.c_str()); 323 | response.platform_response = PLATFORM_CALL_FAIL; 324 | CDM_DLOG() << "RpcCdmPlatformHandler connection to server failed"; 325 | return response; 326 | } else { 327 | CDM_DLOG() << "RpcCdmPlatformHandler connected to server"; 328 | } 329 | // Cdm_MediaKeys 330 | rpc_response_generic *rpc_response; 331 | rpc_request_mediakeys rpc_param; 332 | rpc_param.key_system.key_system_val = reinterpret_cast( 333 | malloc(key_system.size())); 334 | memcpy(rpc_param.key_system.key_system_val, key_system.c_str(), 335 | key_system.size()); 336 | rpc_param.key_system.key_system_len = key_system.size(); 337 | if ((rpc_response = rpc_open_cdm_mediakeys_1(&rpc_param, rpc_client)) 338 | == NULL) { 339 | clnt_perror(rpc_client, rpc_server_host.c_str()); 340 | } 341 | 342 | if (rpc_response->platform_val == 0) { 343 | CDM_DLOG() << "cdm_mediakeys_rpc_1 success\n "; 344 | response.platform_response = PLATFORM_CALL_SUCCESS; 345 | } else { 346 | CDM_DLOG() << "cdm_mediakeys_rpc_1 failed\n "; 347 | response.platform_response = PLATFORM_CALL_FAIL; 348 | } 349 | 350 | return response; 351 | } 352 | 353 | MediaKeysCreateSessionResponse RpcCdmPlatformHandler::MediaKeysCreateSession( 354 | const std::string& init_data_type, const uint8_t* init_data, 355 | int init_data_length) { 356 | CDM_DLOG() << "RpcCdmPlatformHandler::MediaKeysCreateSession"; 357 | MediaKeysCreateSessionResponse response; 358 | 359 | // rpc not ready 360 | if (com_state == FAULTY) { 361 | response.platform_response = PLATFORM_CALL_FAIL; 362 | CDM_DLOG() 363 | << "RpcCdmPlatformHandler::MediaKeysCreateSession connection state faulty"; 364 | return response; 365 | } 366 | rpc_response_create_session *rpc_response; 367 | rpc_request_create_session rpc_param; 368 | 369 | rpc_param.init_data_type.init_data_type_val = reinterpret_cast(malloc( 370 | init_data_type.size())); 371 | memcpy(rpc_param.init_data_type.init_data_type_val, init_data_type.c_str(), 372 | init_data_type.size()); 373 | rpc_param.init_data_type.init_data_type_len = init_data_type.size(); 374 | 375 | rpc_param.init_data.init_data_val = reinterpret_cast( 376 | malloc(init_data_length)); 377 | memcpy(rpc_param.init_data.init_data_val, init_data, init_data_length); 378 | rpc_param.init_data.init_data_len = init_data_length; 379 | 380 | std::string hostname = "localhost"; 381 | // TODO(ska): specify dynamically, encapsulate RPC 382 | rpc_param.callback_info.hostname.hostname_val = reinterpret_cast( 383 | malloc(hostname.size())); 384 | memcpy(rpc_param.callback_info.hostname.hostname_val, hostname.c_str(), 385 | hostname.size()); 386 | rpc_param.callback_info.hostname.hostname_len = hostname.size(); 387 | rpc_param.callback_info.prog_num = rpc_prog; 388 | rpc_param.callback_info.prog_version = 1; 389 | // TODO(ska): specify dynamically, encapsulate RPC 390 | 391 | CDM_DLOG() << "createsession_rpc_1 "; 392 | 393 | if ((rpc_response = rpc_open_cdm_mediakeys_create_session_1(&rpc_param, 394 | rpc_client)) 395 | == NULL) { 396 | clnt_perror(rpc_client, rpc_server_host.c_str()); 397 | CDM_DLOG() << "error createsession_rpc_1"; 398 | } 399 | 400 | // TODO(ska): parse session_id from csresult into 401 | OpenCdmPlatformSessionId session_id; 402 | if (rpc_response->platform_val == 0) { 403 | CDM_DLOG() << "MediaKeys_CreateSession success\n "; 404 | CDM_DLOG() << "CreateSession sid.len: " <session_id.session_id_len; 405 | CDM_DLOG() << "CreateSession sid[0]: " << rpc_response->session_id.session_id_val[0]; 406 | CDM_DLOG() << "CreateSession sid[1]: " << rpc_response->session_id.session_id_val[1]; 407 | response.sys_err = rpc_response->platform_val; 408 | response.platform_response = PLATFORM_CALL_SUCCESS; 409 | session_id.session_id = rpc_response->session_id.session_id_val; 410 | session_id.session_id_len = rpc_response->session_id.session_id_len; 411 | response.session_id = session_id; 412 | } else { 413 | response.platform_response = PLATFORM_CALL_FAIL; 414 | CDM_DLOG() << "MediaKeys_CreateSession failed\n "; 415 | } 416 | return response; 417 | } 418 | 419 | MediaKeysLoadSessionResponse RpcCdmPlatformHandler::MediaKeysLoadSession( 420 | char *session_id_val, uint32_t session_id_len) { 421 | CDM_DLOG() << "RpcCdmPlatformHandler::MediaKeysLoadSession"; 422 | MediaKeysLoadSessionResponse response; 423 | 424 | rpc_response_generic *rpc_response; 425 | rpc_request_load_session rpc_param; 426 | 427 | // rpc not ready 428 | if (com_state == FAULTY) { 429 | response.platform_response = PLATFORM_CALL_FAIL; 430 | CDM_DLOG() 431 | << "RpcCdmPlatformHandler::MediaKeysLoadSession connection state faulty"; 432 | return response; 433 | } 434 | 435 | rpc_param.session_id.session_id_val = session_id_val; 436 | rpc_param.session_id.session_id_len = session_id_len; 437 | 438 | if ((rpc_response = rpc_open_cdm_mediakeys_load_session_1( 439 | &rpc_param, rpc_client)) == NULL) { 440 | clnt_perror(rpc_client, rpc_server_host.c_str()); 441 | } 442 | 443 | if (rpc_response->platform_val == 0) { 444 | CDM_DLOG() << "MediaKeysLoadSession success\n "; 445 | response.platform_response = PLATFORM_CALL_SUCCESS; 446 | } else { 447 | CDM_DLOG() << "MediaKeysLoadSession failed\n "; 448 | response.platform_response = PLATFORM_CALL_FAIL; 449 | } 450 | 451 | return response; 452 | } 453 | 454 | MediaKeySessionUpdateResponse RpcCdmPlatformHandler::MediaKeySessionUpdate( 455 | const uint8 *pbKey, uint32 cbKey, char *session_id_val, 456 | uint32_t session_id_len) { 457 | CDM_DLOG() << "RpcCdmPlatformHandler::MediaKeySessionUpdate"; 458 | MediaKeySessionUpdateResponse response; 459 | 460 | rpc_response_generic *rpc_response; 461 | rpc_request_session_update rpc_param; 462 | 463 | // rpc not ready 464 | if (com_state == FAULTY) { 465 | response.platform_response = PLATFORM_CALL_FAIL; 466 | CDM_DLOG() 467 | << "RpcCdmPlatformHandler::MediaKeySessionUpdate connection state faulty"; 468 | return response; 469 | } 470 | 471 | rpc_param.session_id.session_id_val = session_id_val; 472 | rpc_param.session_id.session_id_len = session_id_len; 473 | rpc_param.key.key_val = reinterpret_cast(malloc(cbKey)); 474 | memcpy(rpc_param.key.key_val, pbKey, cbKey); 475 | rpc_param.key.key_len = cbKey; 476 | 477 | if ((rpc_response = rpc_open_cdm_mediakeysession_update_1( 478 | &rpc_param, rpc_client)) == NULL) { 479 | clnt_perror(rpc_client, rpc_server_host.c_str()); 480 | } 481 | 482 | if (rpc_response->platform_val == 0) { 483 | CDM_DLOG() << "MediaKeySessionUpdate success\n "; 484 | response.platform_response = PLATFORM_CALL_SUCCESS; 485 | } else { 486 | CDM_DLOG() << "MediaKeySessionUpdate failed\n "; 487 | response.platform_response = PLATFORM_CALL_FAIL; 488 | } 489 | 490 | return response; 491 | } 492 | 493 | MediaKeySessionReleaseResponse RpcCdmPlatformHandler::MediaKeySessionRelease( 494 | char *session_id_val, uint32_t session_id_len) { 495 | CDM_DLOG() << "RpcCdmPlatformHandler::MediaKeySessionRelease"; 496 | MediaKeySessionReleaseResponse response; 497 | 498 | rpc_response_generic *rpc_response; 499 | rpc_request_session_release rpc_param; 500 | 501 | if (com_state == FAULTY) { 502 | response.platform_response = PLATFORM_CALL_FAIL; 503 | CDM_DLOG() 504 | << "RpcCdmPlatformHandler::MediaKeySessionRelease connection state faulty"; 505 | return response; 506 | } 507 | 508 | rpc_param.session_id.session_id_val = session_id_val; 509 | rpc_param.session_id.session_id_len = session_id_len; 510 | 511 | if ((rpc_response = rpc_open_cdm_mediakeysession_release_1(&rpc_param, 512 | rpc_client)) 513 | == NULL) { 514 | clnt_perror(rpc_client, rpc_server_host.c_str()); 515 | } 516 | 517 | if (rpc_response->platform_val == 0) { 518 | CDM_DLOG() << "MediaKeySessionRelease success\n "; 519 | response.platform_response = PLATFORM_CALL_SUCCESS; 520 | } else { 521 | CDM_DLOG() << "MediaKeySessionRelease failed\n "; 522 | response.platform_response = PLATFORM_CALL_FAIL; 523 | } 524 | 525 | return response; 526 | } 527 | 528 | } // namespace media 529 | -------------------------------------------------------------------------------- /src/browser/chrome/open_cdm.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | // Based on file contibuted to the Chromium project 15 | // media/cdm/ppapi/external_clear_key/clear_key_cdm.cc 16 | // License notice of original file: 17 | 18 | // Copyright 2013 The Chromium Authors. All rights reserved. 19 | // Use of this source code is governed by a BSD-style license that can be 20 | // found in the LICENSE file. 21 | 22 | #include "base/json/json_reader.h" 23 | #include "base/values.h" 24 | #include "media/cdm/ppapi/external_open_cdm/browser/chrome/open_cdm.h" 25 | 26 | // Include FFmpeg avformat.h for av_register_all(). 27 | extern "C" { 28 | // Temporarily disable possible loss of data warning. 29 | MSVC_PUSH_DISABLE_WARNING(4244); 30 | #include 31 | MSVC_POP_WARNING(); 32 | } // extern "C" 33 | #include "media/base/cdm_callback_promise.h" 34 | #include "media/cdm/cenc_utils.h" 35 | #include "media/cdm/json_web_key.h" 36 | #include "media/cdm/ppapi/external_open_cdm/mediaengine/open_cdm_mediaengine_factory.h" 37 | #include "media/cdm/ppapi/external_open_cdm/cdm/open_cdm_platform_factory.h" 38 | #include "media/cdm/ppapi/external_open_cdm/common/open_cdm_common.h" 39 | 40 | #include "media/cdm/ppapi/cdm_logging.h" 41 | #include "base/bind.h" 42 | #include "media/base/cdm_key_information.h" 43 | #include "media/base/cdm_promise.h" 44 | #include "media/base/decoder_buffer.h" 45 | #include "media/base/decrypt_config.h" 46 | #include "base/strings/string_number_conversions.h" 47 | 48 | #if defined(OCDM_USE_FFMPEG_DECODER) 49 | 50 | #include "base/at_exit.h" 51 | #include "base/files/file_path.h" 52 | #include "base/path_service.h" 53 | #include "media/base/media.h" 54 | 55 | 56 | 57 | // TODO(tomfinegan): When COMPONENT_BUILD is not defined an AtExitManager must 58 | // exist before the call to InitializeFFmpegLibraries(). This should no longer 59 | // be required after http://crbug.com/91970 because we'll be able to get rid of 60 | // InitializeFFmpegLibraries(). 61 | #if !defined COMPONENT_BUILD 62 | static base::AtExitManager g_at_exit_manager; 63 | #endif // COMPONENT_BUILD 64 | 65 | // TODO(tomfinegan): InitializeFFmpegLibraries() and |g_cdm_module_initialized| 66 | // are required for running in the sandbox, and should no longer be required 67 | // after http://crbug.com/91970 is fixed. 68 | static bool InitializeFFmpegLibraries() { 69 | base::FilePath file_path; 70 | CHECK(PathService::Get(base::DIR_MODULE, &file_path)); 71 | CHECK(media::InitializeMediaLibrary(file_path)); 72 | return true; 73 | } 74 | 75 | static bool g_ffmpeg_lib_initialized = InitializeFFmpegLibraries(); 76 | 77 | #endif // OCDM_USE_FFMPEG_DECODER 78 | 79 | // Renewal message header. For prefixed EME, if a key message starts with 80 | // |kRenewalHeader|, it's a renewal message. Otherwise, it's a key request. 81 | // FIXME(jrummell): Remove this once prefixed EME goes away. 82 | const char kRenewalHeader[] = "RENEWAL"; 83 | 84 | static const int64 kSecondsPerMinute = 60; 85 | static const int64 kMsPerSecond = 1000; 86 | static const int64 kInitialTimerDelayMs = 200; 87 | static const int64 kMaxTimerDelayMs = 1 * kSecondsPerMinute * kMsPerSecond; 88 | 89 | /* Currently we don't return the status from he CDMI if the keys are added. 90 | * Whenever there are no keys added we need 91 | * to make sure that the video decoder receives a kNoKey message instead 92 | * of an error. If we return an error, the video decoder will choke on 93 | * it and won' wait for the key to be added. 94 | * 95 | */ 96 | static bool keysAddedToCdm = false; 97 | 98 | //const unsigned int kMaxOpenCDMSessionCount = 1; 99 | 100 | static media::MediaKeys::SessionType ConvertSessionType( 101 | cdm::SessionType session_type) { 102 | switch (session_type) { 103 | case cdm::kTemporary: 104 | return media::MediaKeys::TEMPORARY_SESSION; 105 | case cdm::kPersistentLicense: 106 | return media::MediaKeys::PERSISTENT_LICENSE_SESSION; 107 | case cdm::kPersistentKeyRelease: 108 | return media::MediaKeys::PERSISTENT_RELEASE_MESSAGE_SESSION; 109 | } 110 | NOTIMPLEMENTED(); 111 | return media::MediaKeys::TEMPORARY_SESSION; 112 | } 113 | // Copies |input_buffer| into a media::DecoderBuffer. If the |input_buffer| is 114 | // empty, an empty (end-of-stream) media::DecoderBuffer is returned. 115 | static scoped_refptr CopyDecoderBufferFrom( 116 | const cdm::InputBuffer& input_buffer) { 117 | if (!input_buffer.data) { 118 | DCHECK(!input_buffer.data_size); 119 | return media::DecoderBuffer::CreateEOSBuffer(); 120 | } 121 | 122 | // TODO(xhwang): Get rid of this copy. 123 | scoped_refptr output_buffer = 124 | media::DecoderBuffer::CopyFrom(input_buffer.data, input_buffer.data_size); 125 | 126 | std::vector subsamples; 127 | for (uint32_t i = 0; i < input_buffer.num_subsamples; ++i) { 128 | media::SubsampleEntry subsample; 129 | subsample.clear_bytes = input_buffer.subsamples[i].clear_bytes; 130 | subsample.cypher_bytes = input_buffer.subsamples[i].cipher_bytes; 131 | subsamples.push_back(subsample); 132 | } 133 | 134 | scoped_ptr decrypt_config( 135 | new media::DecryptConfig( 136 | std::string(reinterpret_cast(input_buffer.key_id), 137 | input_buffer.key_id_size), 138 | std::string(reinterpret_cast(input_buffer.iv), 139 | input_buffer.iv_size), 140 | subsamples)); 141 | 142 | output_buffer->set_decrypt_config(decrypt_config.Pass()); 143 | output_buffer->set_timestamp( 144 | base::TimeDelta::FromMicroseconds(input_buffer.timestamp)); 145 | 146 | return output_buffer; 147 | } 148 | 149 | // OVERRIDE from content_decryption_modue.h 150 | void INITIALIZE_CDM_MODULE() { 151 | #if defined(OCDM_USE_FFMPEG_DECODER) 152 | av_register_all(); 153 | #endif // OCDM_USE_FFMPEG_DECODER 154 | } 155 | 156 | void DeinitializeCdmModule() { 157 | } 158 | 159 | static cdm::Error ConvertException(media::MediaKeys::Exception exception_code) { 160 | switch (exception_code) { 161 | case media::MediaKeys::NOT_SUPPORTED_ERROR: 162 | return cdm::kNotSupportedError; 163 | case media::MediaKeys::INVALID_STATE_ERROR: 164 | return cdm::kInvalidStateError; 165 | case media::MediaKeys::INVALID_ACCESS_ERROR: 166 | return cdm::kInvalidAccessError; 167 | case media::MediaKeys::QUOTA_EXCEEDED_ERROR: 168 | return cdm::kQuotaExceededError; 169 | case media::MediaKeys::UNKNOWN_ERROR: 170 | return cdm::kUnknownError; 171 | case media::MediaKeys::CLIENT_ERROR: 172 | return cdm::kClientError; 173 | case media::MediaKeys::OUTPUT_ERROR: 174 | return cdm::kOutputError; 175 | } 176 | NOTIMPLEMENTED(); 177 | return cdm::kUnknownError; 178 | } 179 | cdm::KeyStatus ConvertKeyStatus(media::CdmKeyInformation::KeyStatus status) { 180 | switch (status) { 181 | case media::CdmKeyInformation::KeyStatus::USABLE: 182 | { 183 | return cdm::kUsable; 184 | } 185 | case media::CdmKeyInformation::KeyStatus::INTERNAL_ERROR: 186 | return cdm::kInternalError; 187 | case media::CdmKeyInformation::KeyStatus::EXPIRED: 188 | return cdm::kExpired; 189 | case media::CdmKeyInformation::KeyStatus::OUTPUT_NOT_ALLOWED: 190 | return cdm::kOutputNotAllowed; 191 | case media::CdmKeyInformation::KeyStatus::OUTPUT_DOWNSCALED: 192 | return cdm::kOutputDownscaled; 193 | case media::CdmKeyInformation::KeyStatus::KEY_STATUS_PENDING: 194 | return cdm::kStatusPending; 195 | } 196 | NOTREACHED(); 197 | return cdm::kInternalError; 198 | } 199 | // Shallow copy all the key information from |keys_info| into |keys_vector|. 200 | // |keys_vector| is only valid for the lifetime of |keys_info| because it 201 | // contains pointers into the latter. 202 | void ConvertCdmKeysInfo(const std::vector& keys_info, 203 | std::vector* keys_vector) { 204 | keys_vector->reserve(keys_info.size()); 205 | for (const auto& key_info : keys_info) { 206 | cdm::KeyInformation key; 207 | key.key_id = vector_as_array(&key_info->key_id); 208 | key.key_id_size = key_info->key_id.size(); 209 | key.status = ConvertKeyStatus(key_info->status); 210 | key.system_code = key_info->system_code; 211 | keys_vector->push_back(key); 212 | } 213 | } 214 | void* CreateCdmInstance(int cdm_interface_version, const char* key_system, 215 | uint32_t key_system_size, 216 | GetCdmHostFunc get_cdm_host_func, void* user_data) { 217 | std::string key_system_string(key_system, key_system_size); 218 | 219 | if (key_system_string != media::kExternalOpenCdmKeySystem) { 220 | return NULL; 221 | } 222 | 223 | if (cdm_interface_version != media::OpenCdmInterface::kVersion) 224 | return NULL; 225 | 226 | media::OpenCdmHost* host = static_cast(get_cdm_host_func( 227 | media::OpenCdmHost::kVersion, user_data)); 228 | if (!host) 229 | return NULL; 230 | 231 | return new media::OpenCdm(host, key_system_string); 232 | } 233 | 234 | const char* GetCdmVersion() { 235 | return media::kOpenCdmVersion; 236 | } 237 | 238 | namespace media { 239 | 240 | static std::string ConvertInitDataType( 241 | cdm::InitDataType init_data_type) { 242 | switch (init_data_type) { 243 | case cdm::kCenc: 244 | return "InitDataType::CENC"; 245 | case cdm::kKeyIds: 246 | return "InitDataType::KEYIDS"; 247 | case cdm::kWebM: 248 | return "InitDataType::WEBM"; 249 | } 250 | NOTREACHED(); 251 | return "InitDataType::UNKNOWN"; 252 | } 253 | 254 | static uint32_t next_web_session_id_ = 1; 255 | 256 | OpenCdm::OpenCdm(OpenCdmHost* host, const std::string& key_system) 257 | : host_(host), 258 | media_engine_(NULL), 259 | key_system_(key_system), 260 | timer_delay_ms_(kInitialTimerDelayMs), 261 | renewal_timer_set_(false) 262 | { 263 | 264 | CDM_DLOG() << "OpenDecryptor construct: key_system"; 265 | audio_decoder_state_ = cdm::kDeferredInitialization; 266 | video_decoder_state_ = cdm::kDeferredInitialization; 267 | 268 | platform_ = scoped_ptr( 269 | OpenCdmPlatformInterfaceFactory::Create(this)); 270 | 271 | platform_->MediaKeys(key_system); 272 | CDM_DLOG() << "OpenCdm: created" << "\n"; 273 | } 274 | 275 | OpenCdm::~OpenCdm() { 276 | } 277 | 278 | void OpenCdm::ReadyCallback(OpenCdmPlatformSessionId platform_session_id) { 279 | CDM_DLOG() << "OpenCdm::ReadyCallback"; 280 | CdmKeysInfo keys_info; //We pass empty keys_info 281 | std::vector keys_vector; 282 | keysAddedToCdm = true; 283 | OnSessionKeysUpdate(GetChromeSessionId(platform_session_id), true, keys_info.Pass()); 284 | } 285 | 286 | void OpenCdm::LoadSession(uint32 promise_id, 287 | cdm::SessionType session_type, 288 | const char* web_session_id, 289 | uint32_t web_session_id_length) { 290 | CDM_DLOG() << __FUNCTION__; 291 | NOTIMPLEMENTED(); 292 | } 293 | 294 | void OpenCdm::RemoveSession(uint32 promise_id, 295 | const char* web_session_id, 296 | uint32_t web_session_id_length) { 297 | CDM_DLOG() << __FUNCTION__; 298 | NOTIMPLEMENTED(); 299 | } 300 | 301 | void OpenCdm::ErrorCallback(OpenCdmPlatformSessionId platform_session_id, 302 | uint32_t sys_err, std::string err_msg) { 303 | CDM_DLOG() << "OpenCdm::ErrorCallback"; 304 | } 305 | 306 | void OpenCdm::MessageCallback(OpenCdmPlatformSessionId platform_session_id, 307 | std::string message, 308 | std::string destination_url) { 309 | CDM_DLOG() << "OpenCdm::MessageCallback"; 310 | } 311 | 312 | void OpenCdm::OnKeyStatusUpdateCallback(OpenCdmPlatformSessionId platform_session_id, 313 | std::string message) 314 | { 315 | scoped_ptr root(base::JSONReader().ReadToValue(message)); 316 | 317 | 318 | if (!root.get() || root->GetType() != base::Value::TYPE_DICTIONARY) { 319 | return; 320 | } 321 | base::DictionaryValue* dict = 322 | static_cast(root.get()); 323 | CDM_DLOG() << *dict; 324 | CdmKeysInfo keys_info; 325 | for (base::DictionaryValue::Iterator itr(*dict); !itr.IsAtEnd(); 326 | itr.Advance()) 327 | { 328 | scoped_ptr key_info(new CdmKeyInformation); 329 | key_info->key_id.assign(itr.key().begin(), itr.key().end()); 330 | 331 | /* FIXME: We ignore returned key statuses. Set all keys usable. 332 | * We have to pull in the key status defines from cdmi.h to 333 | * process the returned key states properly. 334 | */ 335 | 336 | key_info->status = CdmKeyInformation::USABLE; 337 | key_info->system_code = 0; 338 | keys_info.push_back(key_info.release()); 339 | } 340 | OnSessionKeysUpdate(GetChromeSessionId(platform_session_id), true, keys_info.Pass()); 341 | CDM_DLOG() << "Got key status update : %s:" << message; 342 | } 343 | 344 | void OpenCdm::OnSessionKeysUpdate(const std::string& session_id, 345 | bool has_additional_usable_key, 346 | CdmKeysInfo keys_info) { 347 | std::string new_session_id = session_id; 348 | 349 | std::vector keys_vector; 350 | ConvertCdmKeysInfo(keys_info.get(), &keys_vector); 351 | host_->OnSessionKeysChange(new_session_id.data(), new_session_id.length(), 352 | has_additional_usable_key, 353 | vector_as_array(&keys_vector), keys_vector.size()); 354 | } 355 | 356 | 357 | void OpenCdm::Initialize(bool allow_distinctive_identifier, bool allow_persistent_state) 358 | { 359 | 360 | } 361 | 362 | void OpenCdm::CreateSessionAndGenerateRequest(uint32 promise_id, 363 | cdm::SessionType session_type, 364 | cdm::InitDataType init_data_type, 365 | const uint8* init_data, 366 | uint32 init_data_size) { 367 | CDM_DLOG() << " OpenCdm::CreateSession promise_id: " << promise_id 368 | << " - session_type: " << session_type; 369 | 370 | scoped_ptr promise( 371 | new media::CdmCallbackPromise( 372 | base::Bind(&OpenCdm::OnSessionCreated, base::Unretained(this), 373 | promise_id), 374 | base::Bind(&OpenCdm::OnPromiseFailed, base::Unretained(this), 375 | promise_id))); 376 | 377 | CDM_DLOG() << "OpenCdmDecryptor::CreateSession"; 378 | 379 | uint32_t renderer_session_id = next_web_session_id_++; 380 | std::vector> keys; 381 | std::string web_session_id = base::UintToString(renderer_session_id); 382 | 383 | if (init_data && init_data_size) { 384 | switch (init_data_type) { 385 | case cdm::kWebM: 386 | keys.push_back( 387 | std::vector(init_data, init_data + init_data_size)); 388 | 389 | break; 390 | case cdm::kCenc: 391 | if (!GetKeyIdsForCommonSystemId(init_data, init_data_size, &keys)) { 392 | promise->reject(MediaKeys::NOT_SUPPORTED_ERROR, 0, 393 | "No supported PSSH box found."); 394 | return; 395 | } 396 | break; 397 | case cdm::kKeyIds: { 398 | std::string init_data_string(init_data, init_data + init_data_size); 399 | std::string error_message; 400 | if (!ExtractKeyIdsFromKeyIdsInitData(init_data_string, &keys, 401 | &error_message)) { 402 | promise->reject(MediaKeys::NOT_SUPPORTED_ERROR, 0, error_message); 403 | return; 404 | } 405 | break; 406 | } 407 | default: 408 | NOTREACHED(); 409 | promise->reject(MediaKeys::NOT_SUPPORTED_ERROR, 0, 410 | "init_data_type not supported."); 411 | return; 412 | } 413 | } 414 | MediaKeysCreateSessionResponse response = platform_->MediaKeysCreateSession( 415 | ConvertInitDataType(init_data_type), init_data, init_data_size); 416 | 417 | if (response.platform_response == PLATFORM_CALL_SUCCESS) { 418 | this->session_id_map[web_session_id] = response.session_id; 419 | std::string debug_web_session_id = GetChromeSessionId(response.session_id); 420 | CDM_DLOG() << "New Session promise resolved, last_session_id_: " 421 | << web_session_id; 422 | promise->resolve(web_session_id); 423 | } else { 424 | CDM_DLOG() << "reject create session promise"; 425 | promise->reject(media::MediaKeys::INVALID_STATE_ERROR, 426 | response.platform_response, 427 | "MediaKeySession could not be created."); 428 | return; 429 | } 430 | /* Key request */ 431 | 432 | std::vector message; 433 | const GURL& legacy_destination_url = GURL::EmptyGURL(); 434 | 435 | if (init_data && init_data_size) 436 | CreateLicenseRequest(keys, ConvertSessionType(session_type), &message); 437 | 438 | CDM_DLOG() << " Request LicenseRequest\n"; 439 | 440 | host_->OnSessionMessage(web_session_id.data(), web_session_id.length(), 441 | cdm::kLicenseRequest, 442 | reinterpret_cast(message.data()), 443 | message.size(), legacy_destination_url.spec().data(), 444 | legacy_destination_url.spec().size()); 445 | return; 446 | 447 | } 448 | 449 | void OpenCdm::UpdateSession(uint32 promise_id, const char* web_session_id, 450 | uint32_t web_session_id_size, const uint8* response, 451 | uint32 response_size) { 452 | CDM_DLOG() << " OpenCdm::UpdateSession for " << web_session_id; 453 | 454 | std::string web_session_str(web_session_id, web_session_id_size); 455 | 456 | scoped_ptr promise(new media::CdmCallbackPromise<>( 457 | base::Bind(&OpenCdm::OnPromiseResolved, base::Unretained(this), 458 | promise_id), 459 | base::Bind(&OpenCdm::OnPromiseFailed, base::Unretained(this), 460 | promise_id))); 461 | 462 | CDM_DLOG() << " OpenCdmDecryptor::UpdateSession "; 463 | 464 | CHECK(response); 465 | 466 | CDM_DLOG() << "UpdateSession: response_length: " << response_size; 467 | CDM_DLOG() << "UpdateSession: web_session_id: " << web_session_id; 468 | 469 | if (session_id_map.find(web_session_id) != session_id_map.end()) { 470 | platform_->MediaKeySessionUpdate( 471 | response, response_size, session_id_map[web_session_id].session_id, 472 | session_id_map[web_session_id].session_id_len); 473 | promise->resolve(); 474 | } else { 475 | promise->reject(media::MediaKeys::INVALID_ACCESS_ERROR, 0, 476 | "Session does not exist."); 477 | return; 478 | } 479 | 480 | /* 481 | *FIXME: disabled for now. Causes issues with the CDMI service. 482 | if (!renewal_timer_set_) { 483 | ScheduleNextRenewal(); 484 | renewal_timer_set_ = true; 485 | } 486 | */ 487 | keysAddedToCdm = true; 488 | } 489 | 490 | void OpenCdm::ScheduleNextRenewal() { 491 | std::ostringstream msg_stream; 492 | msg_stream << kRenewalHeader << " from OpenCDM set at time " 493 | << host_->GetCurrentWallTime() << "."; 494 | next_renewal_message_ = msg_stream.str(); 495 | 496 | host_->SetTimer(timer_delay_ms_, &next_renewal_message_[0]); 497 | 498 | // Use a smaller timer delay at start-up to facilitate testing. Increase the 499 | // timer delay up to a limit to avoid message spam. 500 | if (timer_delay_ms_ < kMaxTimerDelayMs) 501 | timer_delay_ms_ = std::min(2 * timer_delay_ms_, kMaxTimerDelayMs); 502 | } 503 | 504 | void OpenCdm::CloseSession(uint32 promise_id, 505 | const char* web_session_id, 506 | uint32_t web_session_id_length) { 507 | 508 | CDM_DLOG() << " OpenCdm::CloseSession"; 509 | scoped_ptr promise(new media::CdmCallbackPromise<>( 510 | base::Bind( 511 | &OpenCdm::OnPromiseResolved, base::Unretained(this), promise_id), 512 | base::Bind( 513 | &OpenCdm::OnPromiseFailed, base::Unretained(this), promise_id))); 514 | 515 | if (session_id_map.find(web_session_id) != session_id_map.end()) { 516 | platform_->MediaKeySessionRelease( 517 | session_id_map[web_session_id].session_id, 518 | session_id_map[web_session_id].session_id_len); 519 | std::map::iterator iterator = 520 | session_id_map.find(web_session_id); 521 | session_id_map.erase(iterator); 522 | promise->resolve(); 523 | } else { 524 | promise->reject(media::MediaKeys::INVALID_ACCESS_ERROR, 0, 525 | "Session does not exist."); 526 | return; 527 | } 528 | } 529 | 530 | void OpenCdm::SetServerCertificate(uint32 promise_id, 531 | const uint8_t* server_certificate_data, 532 | uint32_t server_certificate_data_size) { 533 | CDM_DLOG() << " OpenCdm::SetServerCertificate "; 534 | NOTIMPLEMENTED(); 535 | } 536 | 537 | void OpenCdm::TimerExpired(void* context) { 538 | 539 | DCHECK(renewal_timer_set_); 540 | std::string renewal_message; 541 | if (!next_renewal_message_.empty() && 542 | context == &next_renewal_message_[0]) { 543 | renewal_message = next_renewal_message_; 544 | } else { 545 | renewal_message = "ERROR: Invalid timer context found!"; 546 | } 547 | 548 | // This URL is only used for testing the code path for defaultURL. 549 | // There is no service at this URL, so applications should ignore it. 550 | const char url[] = "http://test.externalclearkey.chromium.org"; 551 | 552 | host_->OnSessionMessage(last_session_id_.data(), last_session_id_.length(), 553 | cdm::kLicenseRenewal, renewal_message.data(), 554 | renewal_message.length(), url, arraysize(url) - 1); 555 | 556 | //ScheduleNextRenewal(); 557 | } 558 | 559 | enum ClearBytesBufferSel { 560 | kSrcContainsClearBytes, 561 | kDstContainsClearBytes 562 | }; 563 | 564 | static void CopySubsamples(const std::vector& subsamples, 565 | const ClearBytesBufferSel sel, const uint8* src, 566 | uint8* dst) { 567 | for (size_t i = 0; i < subsamples.size(); i++) { 568 | const SubsampleEntry& subsample = subsamples[i]; 569 | if (sel == kSrcContainsClearBytes) { 570 | src += subsample.clear_bytes; 571 | } else { 572 | dst += subsample.clear_bytes; 573 | } 574 | memcpy(dst, src, subsample.cypher_bytes); 575 | src += subsample.cypher_bytes; 576 | dst += subsample.cypher_bytes; 577 | } 578 | } 579 | 580 | cdm::Status OpenCdm::Decrypt(const cdm::InputBuffer& encrypted_buffer, 581 | cdm::DecryptedBlock* decrypted_block) { 582 | CDM_DLOG() << "OpenCdm::Decrypt()"; 583 | 584 | DCHECK(encrypted_buffer.data); 585 | 586 | scoped_refptr buffer; 587 | cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer); 588 | 589 | if (status != cdm::kSuccess) 590 | return status; 591 | 592 | DCHECK(buffer->data()); 593 | decrypted_block->SetDecryptedBuffer(host_->Allocate(buffer->data_size())); 594 | memcpy(reinterpret_cast(decrypted_block->DecryptedBuffer()->Data()), 595 | buffer->data(), buffer->data_size()); 596 | decrypted_block->DecryptedBuffer()->SetSize(buffer->data_size()); 597 | decrypted_block->SetTimestamp(buffer->timestamp().InMicroseconds()); 598 | 599 | return cdm::kSuccess; 600 | } 601 | 602 | void OpenCdm::OnPromiseResolved(uint32 promise_id) { 603 | host_->OnResolvePromise(promise_id); 604 | } 605 | 606 | cdm::Status OpenCdm::InitializeAudioDecoder( 607 | const cdm::AudioDecoderConfig& audio_decoder_config) { 608 | CDM_DLOG() << "OpenCdm::InitializeAudioDecoder()"; 609 | 610 | if (key_system_ == media::kExternalOpenCdmKeySystem) 611 | return cdm::kSessionError; 612 | 613 | #if defined(OCDM_USE_FFMPEG_DECODER) 614 | if (!audio_decoder_) 615 | audio_decoder_.reset(new media::FFmpegCdmAudioDecoder(host_)); 616 | 617 | if (!audio_decoder_->Initialize(audio_decoder_config)) 618 | return cdm::kSessionError; 619 | 620 | return cdm::kSuccess; 621 | #elif defined(OPEN_CDM_USE_FAKE_AUDIO_DECODER) 622 | channel_count_ = audio_decoder_config.channel_count; 623 | bits_per_channel_ = audio_decoder_config.bits_per_channel; 624 | samples_per_second_ = audio_decoder_config.samples_per_second; 625 | return cdm::kSuccess; 626 | #else 627 | NOTIMPLEMENTED(); 628 | return cdm::kSessionError; 629 | #endif 630 | } 631 | 632 | cdm::Status OpenCdm::InitializeVideoDecoder( 633 | const cdm::VideoDecoderConfig& video_decoder_config) { 634 | CDM_DLOG() << "OpenCdm::InitializeVideoDecoder()"; 635 | last_stream_type_ = cdm::kStreamTypeVideo; 636 | 637 | #if defined(OCDM_USE_FFMPEG_DECODER) 638 | if (video_decoder_ && video_decoder_->is_initialized()) { 639 | DCHECK(!video_decoder_->is_initialized()); 640 | return cdm::kSessionError; 641 | } 642 | // Any uninitialized decoder will be replaced. 643 | video_decoder_ = CreateVideoDecoder(host_, video_decoder_config); 644 | if (!video_decoder_) 645 | return cdm::kSessionError; 646 | CDM_DLOG() << "VideoDecoder initialized"; 647 | 648 | return cdm::kSuccess; 649 | #else 650 | NOTIMPLEMENTED(); 651 | return cdm::kSessionError; 652 | #endif // OCDM_USE_FFMPEG_DECODER 653 | } 654 | 655 | void OpenCdm::ResetDecoder(cdm::StreamType decoder_type) { 656 | CDM_DLOG() << "OpenCdm::ResetDecoder()"; 657 | #if defined(OCDM_USE_FFMPEG_DECODER) 658 | switch (decoder_type) { 659 | case cdm::kStreamTypeVideo: 660 | video_decoder_->Reset(); 661 | break; 662 | case cdm::kStreamTypeAudio: 663 | audio_decoder_->Reset(); 664 | break; 665 | default: 666 | NOTREACHED() << "ResetDecoder(): invalid cdm::StreamType"; 667 | } 668 | #endif // OCDM_USE_FFMPEG_DECODER 669 | } 670 | 671 | void OpenCdm::DeinitializeDecoder(cdm::StreamType decoder_type) { 672 | CDM_DLOG() << "OpenCdm::DeinitializeDecoder()"; 673 | switch (decoder_type) { 674 | #if defined(OCDM_USE_FFMPEG_DECODER) 675 | case cdm::kStreamTypeVideo: 676 | video_decoder_->Deinitialize(); 677 | break; 678 | case cdm::kStreamTypeAudio: 679 | audio_decoder_->Deinitialize(); 680 | #endif 681 | break; 682 | default: 683 | NOTREACHED() << "DeinitializeDecoder(): invalid cdm::StreamType"; 684 | } 685 | } 686 | 687 | cdm::Status OpenCdm::DecryptAndDecodeFrame( 688 | const cdm::InputBuffer& encrypted_buffer, cdm::VideoFrame* decoded_frame) { 689 | CDM_DLOG() << "OpenCdm::DecryptAndDecodeFrame()"; 690 | #if defined(OCDM_USE_FFMPEG_DECODER) 691 | scoped_refptr buffer; 692 | cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer); 693 | 694 | if (status != cdm::kSuccess) 695 | return status; 696 | 697 | const uint8_t* data = NULL; 698 | int32_t size = 0; 699 | int64_t timestamp = 0; 700 | if (!buffer->end_of_stream()) { 701 | data = buffer->data(); 702 | size = buffer->data_size(); 703 | timestamp = encrypted_buffer.timestamp; 704 | } 705 | 706 | return video_decoder_->DecodeFrame(data, size, timestamp, decoded_frame); 707 | #else 708 | return cdm::kDecryptError; 709 | #endif 710 | } 711 | 712 | cdm::Status OpenCdm::DecryptAndDecodeSamples( 713 | const cdm::InputBuffer& encrypted_buffer, cdm::AudioFrames* audio_frames) { 714 | CDM_DLOG() << "OpenCdm::DecryptAndDecodeSamples()"; 715 | 716 | scoped_refptr buffer; 717 | cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer); 718 | 719 | if (status != cdm::kSuccess) 720 | return status; 721 | 722 | #if defined(OCDM_USE_FFMPEG_DECODER) 723 | const uint8_t* data = NULL; 724 | int32_t size = 0; 725 | int64_t timestamp = 0; 726 | if (!buffer->end_of_stream()) { 727 | data = buffer->data(); 728 | size = buffer->data_size(); 729 | timestamp = encrypted_buffer.timestamp; 730 | } 731 | 732 | return audio_decoder_->DecodeBuffer(data, size, timestamp, audio_frames); 733 | #else 734 | return cdm::kDecryptError; 735 | #endif // OCDM_USE_FFMPEG_DECODER 736 | } 737 | 738 | void OpenCdm::Destroy() { 739 | CDM_DLOG() << "OpenCdm::Destroy()" << "\n"; 740 | delete this; 741 | } 742 | 743 | OpenCdmPlatformSessionId OpenCdm::GetPlatformSessionId( 744 | const std::string& web_session_id) { 745 | CDM_DLOG() << "OpenCdm::GetPlatformSessionId" << "\n"; 746 | 747 | OpenCdmPlatformSessionId session_id; 748 | 749 | if (session_id_map.find(web_session_id) != session_id_map.end()) { 750 | session_id = session_id_map[web_session_id]; 751 | } 752 | 753 | return session_id; 754 | } 755 | 756 | std::string OpenCdm::GetChromeSessionId( 757 | OpenCdmPlatformSessionId platform_session_id) { 758 | CDM_DLOG() << "OpenCdm::GetChromeSessionId"; 759 | CDM_DLOG() << "OpenCdm::GetChromeSessionId, sid.len: " 760 | << platform_session_id.session_id_len; 761 | CDM_DLOG() << "OpenCdm::GetChromeSessionId, sid[0]: " 762 | << platform_session_id.session_id[0]; 763 | CDM_DLOG() << "OpenCdm::GetChromeSessionId, sid[1]: " 764 | << platform_session_id.session_id[1]; 765 | std::string web_session_id = ""; 766 | 767 | std::map::const_iterator it; 768 | for (it = session_id_map.begin(); it != session_id_map.end(); it++) { 769 | if (it->second.session_id_len == platform_session_id.session_id_len) { 770 | bool equal = false; 771 | for (uint32_t i_cmp = 0; i_cmp < it->second.session_id_len; i_cmp++) { 772 | equal = (it->second.session_id[i_cmp] 773 | == platform_session_id.session_id[i_cmp]); 774 | if (!equal) { 775 | break; 776 | } 777 | } 778 | if (equal) { 779 | web_session_id = it->first; 780 | break; 781 | } 782 | } 783 | } 784 | CDM_DLOG() << "OpenCdm:: web_session_id " << web_session_id; 785 | return web_session_id; 786 | } 787 | 788 | cdm::Status OpenCdm::DecryptToMediaDecoderBuffer( 789 | const cdm::InputBuffer& encrypted_buffer, 790 | scoped_refptr* decrypted_buffer) { 791 | CDM_DLOG() << "OpenCdm::DecryptToMediaDecoderBuffer()"; 792 | DCHECK(decrypted_buffer); 793 | 794 | //Fixme: We need to remove the memcopy 795 | scoped_ptr out(new uint8[encrypted_buffer.data_size]); 796 | uint32_t out_size = -1; 797 | 798 | if(!keysAddedToCdm) 799 | return cdm::kNoKey; 800 | 801 | scoped_refptr buffer = CopyDecoderBufferFrom( 802 | encrypted_buffer); 803 | 804 | if (buffer->end_of_stream()) { 805 | CDM_DLOG() << "#buffer->end_of_stream()"; 806 | *decrypted_buffer = buffer; 807 | return cdm::kSuccess; 808 | } 809 | 810 | // from AESDecryptor.decrypt 811 | // An empty iv string signals that the frame is unencrypted. 812 | if (buffer->decrypt_config()->iv().empty()) { 813 | CDM_DLOG() << "#buffer->decrypt_config()->iv().empty()"; 814 | *decrypted_buffer = buffer; 815 | return cdm::kSuccess; 816 | } 817 | 818 | // mediaengine instantiation 819 | if (!media_engine_) { 820 | // TODO(ska): handle mutiple sessions 821 | OpenCdmPlatformSessionId session_id = GetPlatformSessionId( 822 | last_session_id_); 823 | media_engine_ = OpenCdmMediaengineFactory::Create(key_system_, session_id); 824 | if(!media_engine_) 825 | return cdm::kDecryptError; 826 | } 827 | 828 | // from AESDecryptor.decryptData 829 | 830 | const char* sample = reinterpret_cast(buffer.get()->data()); 831 | size_t sample_size = static_cast(buffer.get()->data_size()); 832 | 833 | if (sample_size == 0) { 834 | CDM_DLOG() << "sample_size == 0"; 835 | return cdm::kDecryptError; 836 | } 837 | 838 | if (buffer.get()->decrypt_config()->subsamples().empty()) { 839 | DecryptResponse dr = media_engine_->Decrypt(encrypted_buffer.iv, 840 | encrypted_buffer.iv_size, 841 | encrypted_buffer.data, 842 | sample_size, 843 | out.get(), 844 | out_size); 845 | //FIXME: Redundant buffer copy 846 | if (dr.platform_response == PLATFORM_CALL_SUCCESS) { 847 | *decrypted_buffer = DecoderBuffer::CopyFrom(reinterpret_cast(out.get()), out_size); 848 | return cdm::kSuccess; 849 | } else { 850 | return cdm::kDecryptError; 851 | } 852 | } 853 | 854 | const std::vector& subsamples = buffer->decrypt_config() 855 | ->subsamples(); 856 | 857 | size_t total_clear_size = 0; 858 | size_t total_encrypted_size = 0; 859 | CDM_DLOG() << " Subsamples size: " << subsamples.size(); 860 | 861 | for (size_t i = 0; i < subsamples.size(); i++) { 862 | total_clear_size += subsamples[i].clear_bytes; 863 | total_encrypted_size += subsamples[i].cypher_bytes; 864 | // Check for overflow. This check is valid because *_size is unsigned. 865 | DCHECK(total_clear_size >= subsamples[i].clear_bytes); 866 | if (total_encrypted_size < subsamples[i].cypher_bytes) { 867 | CDM_DLOG() << "#total_encrypted_size < subsamples[i].cypher_bytes"; 868 | return cdm::kDecryptError; 869 | } 870 | } 871 | size_t total_size = total_clear_size + total_encrypted_size; 872 | if (total_size < total_clear_size || total_size != sample_size) { 873 | CDM_DLOG() << "#Subsample sizes do not equal input size" ; 874 | return cdm::kDecryptError; 875 | } 876 | 877 | // No need to decrypt if there is no encrypted data. 878 | if (total_encrypted_size <= 0) { 879 | CDM_DLOG() << "#No need to decrypt no encrypted data"; 880 | *decrypted_buffer = DecoderBuffer::CopyFrom( 881 | reinterpret_cast(sample), sample_size); 882 | return cdm::kSuccess; 883 | } 884 | 885 | // The encrypted portions of all subsamples must form a contiguous block, 886 | // such that an encrypted subsample that ends away from a block boundary is 887 | // immediately followed by the start of the next encrypted subsample. We 888 | // copy all encrypted subsamples to a contiguous buffer, decrypt them, then 889 | // copy the decrypted bytes over the encrypted bytes in the output. 890 | // TODO(strobe): attempt to reduce number of memory copies 891 | scoped_ptr encrypted_bytes(new uint8[total_encrypted_size]); 892 | CopySubsamples(subsamples, kSrcContainsClearBytes, 893 | reinterpret_cast(sample), encrypted_bytes.get()); 894 | 895 | 896 | DecryptResponse dr = media_engine_->Decrypt(encrypted_buffer.iv, 897 | encrypted_buffer.iv_size, 898 | encrypted_bytes.get(), 899 | total_encrypted_size, out.get(), 900 | out_size); 901 | CDM_DLOG() << "media_engine_->Decrypt done"; 902 | 903 | DCHECK_EQ(out_size, total_encrypted_size); 904 | 905 | scoped_refptr output = DecoderBuffer::CopyFrom( 906 | reinterpret_cast(sample), sample_size); 907 | CopySubsamples(subsamples, kDstContainsClearBytes, out.get(), 908 | output->writable_data()); 909 | 910 | *decrypted_buffer = output; 911 | if (dr.platform_response == PLATFORM_CALL_SUCCESS) { 912 | return cdm::kSuccess; 913 | } else { 914 | return cdm::kDecryptError; 915 | } 916 | } 917 | 918 | void OpenCdm::OnPlatformChallengeResponse( 919 | const cdm::PlatformChallengeResponse& response) { 920 | CDM_DLOG() << "OpenCdm::OnPlatformChallengeResponse"; 921 | NOTIMPLEMENTED(); 922 | } 923 | 924 | void OpenCdm::OnQueryOutputProtectionStatus( 925 | cdm::QueryResult result, 926 | uint32_t link_mask, 927 | uint32_t output_protection_mask) { 928 | NOTIMPLEMENTED(); 929 | }; 930 | 931 | 932 | void OpenCdm::OnSessionCreated(uint32 promise_id, 933 | const std::string& web_session_id) { 934 | CDM_DLOG() << "OpenCdm::OnSessionCreated"; 935 | // Save the latest session ID for heartbeat and file IO test messages. 936 | last_session_id_ = web_session_id; 937 | 938 | host_->OnResolveNewSessionPromise(promise_id, web_session_id.data(), 939 | web_session_id.length()); 940 | } 941 | 942 | void OpenCdm::OnSessionLoaded(uint32 promise_id, 943 | const std::string& web_session_id) { 944 | CDM_DLOG() << "OpenCdm::OnSessionLoaded"; 945 | } 946 | 947 | void OpenCdm::OnPromiseFailed(uint32 promise_id, 948 | MediaKeys::Exception exception_code, 949 | uint32 system_code, 950 | const std::string& error_message) { 951 | CDM_DLOG() << "OpenCdm::OnPromiseFailed"; 952 | 953 | host_->OnRejectPromise(promise_id, ConvertException(exception_code), 954 | system_code, error_message.data(), 955 | error_message.length()); 956 | } 957 | 958 | } // namespace media 959 | --------------------------------------------------------------------------------