RDK Documentation (Open Sourced RDK Components)
opencdmsession.cpp
Go to the documentation of this file.
1 /*
2  * If not stated otherwise in this file or this component's license file the
3  * following copyright and licenses apply:
4  *
5  * Copyright 2018 RDK Management
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18 */
19 
20 /**
21  * @file opencdmsession.cpp
22  * @brief Playready Session management
23  */
24 #include "config.h"
25 #include "opencdmsession.h"
26 #include <gst/gst.h>
27 #include <assert.h>
28 #include <iostream>
29 #include <sstream>
30 #include <string>
31 #include <string.h>
32 #include <vector>
33 #include <sys/utsname.h>
34 #include "priv_aamp.h"
35 
36 #include <sys/time.h>
37 #define USEC_PER_SEC 1000000
38 
39 
40 static inline uint64_t GetCurrentTimeStampInUSec()
41 {
42  struct timeval timeStamp;
43  uint64_t retVal = 0;
44 
45  gettimeofday(&timeStamp, NULL);
46 
47  // Convert timestamp to Micro Seconds
48  retVal = (uint64_t)(((uint64_t)timeStamp.tv_sec * USEC_PER_SEC) + timeStamp.tv_usec);
49 
50  return retVal;
51 }
52 
53 
54 static inline uint64_t GetCurrentTimeStampInMSec()
55 {
56  return GetCurrentTimeStampInUSec() / 1000;
57 }
58 
59 #define LOG_DECRYPT_STATS 1
60 #define DECRYPT_AVG_TIME_THRESHOLD 10.0 //10 milliseconds
61 #ifdef LOG_DECRYPT_STATS
62 #define MAX_THREADS 10
63 #define INTERVAL 120
64 
65 /**
66  * @struct DecryptStats
67  * @brief Holds decryption profile stats
68  */
69 struct DecryptStats
70 {
71  uint64_t nBytesInterval;
72  uint64_t nTimeInterval;
73  uint64_t nBytesTotal;
74  uint64_t nTimeTotal;
75  uint64_t nCallsTotal;
76  pthread_t threadID;
77 
78 };
79 #endif // LOG_DECRYPT_STATS
80 #define SEC_SIZE size_t
81 
82 
83 void LogPerformanceExt(const char* strFunc, uint64_t msStart, uint64_t msEnd, SEC_SIZE nDataSize)
84 {
85  bool bThreshold = false;
86  uint64_t delta = msEnd - msStart;
87  uint32_t nRateMin = 1000; /**< Bytes/ms */
88  uint32_t nRestart = 5;
89  uint32_t nDataMin = 1000;
90  uint32_t nTimeMin = 5; /**< Can not be < 1 to protect against divide by 0 error */
91 
92 #ifdef LOG_DECRYPT_STATS
93  {
94  static DecryptStats stats[MAX_THREADS] = { 0 };
95  int idx = 0;
96  while(idx < MAX_THREADS) {
97  if(stats[idx].threadID == pthread_self()) {
98  break;
99  }
100  idx++;
101  }
102  if(idx == MAX_THREADS) {
103  // new thread
104  idx = 0;
105  while(idx < MAX_THREADS) {
106  if(stats[idx].threadID == 0) {
107  // empty slot
108  stats[idx].threadID = pthread_self();
109  break;
110  }
111  idx++;
112  }
113  }
114  if(idx == MAX_THREADS) {
115  AAMPLOG_WARN(">>>>>>>> All slots allocated!!!, idx = %d, clearing the array.", idx);
116  memset(stats, 0, sizeof(DecryptStats) * MAX_THREADS);
117  return;
118  }
119 
120  if(nDataSize > 0 ) {
121  stats[idx].nBytesInterval += (uint64_t)nDataSize;
122  stats[idx].nTimeInterval += delta;
123  stats[idx].nCallsTotal++;
124 
125  if(stats[idx].nCallsTotal % INTERVAL == 0) {
126  stats[idx].nBytesTotal += stats[idx].nBytesInterval;
127  stats[idx].nTimeTotal += stats[idx].nTimeInterval;
128  double avgTime = (double)stats[idx].nTimeTotal/(double)stats[idx].nCallsTotal;
129  if(avgTime >= DECRYPT_AVG_TIME_THRESHOLD) {
130  AAMPLOG_WARN(">>>>>>>> Thread ID %X (%d) Avg Time %0.2llf ms, Avg Bytes %llu calls (%llu) Interval avg time %0.2llf, Interval avg bytes %llu",
131  strFunc, stats[idx].threadID, idx, avgTime, stats[idx].nBytesTotal/stats[idx].nCallsTotal,
132  stats[idx].nCallsTotal, (double)stats[idx].nTimeInterval/(double)INTERVAL,
133  stats[idx].nBytesInterval/INTERVAL);
134  }
135  stats[idx].nBytesInterval = 0;
136  stats[idx].nTimeInterval = 0;
137 
138  }
139  }
140  }
141 #endif //LOG_DECRYPT_STATS
142 }
143 
144 #ifdef USE_SAGE_SVP
145 #include "b_secbuf.h"
146 
147 /**
148  * @struct Rpc_Secbuf_Info
149  * @brief Sec Buffer
150  */
151 struct Rpc_Secbuf_Info {
152  uint8_t *ptr;
153  uint32_t type;
154  size_t size;
155  void *token;
156 };
157 #endif
158 
159 //The following flag is used to use old or new(wpeframework) OpenCDM implementation
160 #define USE_NEW_OPENCDM 1
161 
162 AAMPOCDMSession::AAMPOCDMSession(AampLogManager *logObj, const string& keySystem) :
163  AampDrmSession(logObj, keySystem),
164  m_eKeyState(KEY_INIT),
165  m_pOutputProtection(NULL),
166  m_pOpencdm(NULL),
167  m_pOpencdmDecrypt(NULL),
168  decryptMutex(),
169  m_sessionID()
170 {
171 
172  pthread_mutex_init(&decryptMutex,NULL);
173 
174  initAampDRMSession();
175 
176  // Get output protection pointer
178 
179 }
180 
181 
182 void AAMPOCDMSession::initAampDRMSession()
183 {
184 
185  if (m_pOpencdm == NULL) {
186  m_pOpencdm = new media::OpenCdm();
187  }
188 
189  m_pOpencdm->SelectKeySystem(m_keySystem);
190 
191 }
192 
193 
194 void AAMPOCDMSession::generateAampDRMSession(const uint8_t *f_pbInitData,
195  uint32_t f_cbInitData, std::string &customData)
196 {
197 
198  pthread_mutex_lock(&decryptMutex);
199 #if USE_NEW_OPENCDM
200  m_sessionID = m_pOpencdm->CreateSession("video/mp4", const_cast<unsigned char*>(f_pbInitData), f_cbInitData, media::OpenCdm::Temporary);
201  AAMPLOG_WARN("generateAampDRMSession :: sessionId : %s ", m_sessionID.c_str());
202  if(m_sessionID.empty()) {
203  m_eKeyState = KEY_ERROR_EMPTY_SESSION_ID;
204  }
205 #else
206  std::string sessionId;
207  m_pOpencdm->CreateSession("video/mp4", const_cast<unsigned char*>(f_pbInitData), f_cbInitData, sessionId);
208  AAMPLOG_WARN("generateAampDRMSession :: sessionId : %s ", sessionId.c_str());
209  if(sessionId.empty()) {
210  m_eKeyState = KEY_ERROR_EMPTY_SESSION_ID;
211  }
212 #endif
213  pthread_mutex_unlock(&decryptMutex);
214 }
215 
216 
217 AAMPOCDMSession::~AAMPOCDMSession()
218 {
219  AAMPLOG_WARN("[HHH]OCDMSession destructor called! keySystem %s", m_keySystem.c_str());
221 
222  pthread_mutex_destroy(&decryptMutex);
223 #if USE_NEW_OPENCDM
224  if(m_pOpencdmDecrypt)
225  {
226  m_pOpencdmDecrypt->Close();
227  SAFE_DELETE(m_pOpencdmDecrypt);
228  }
229 #endif
230 
231  if(m_pOpencdm)
232  {
233 #if USE_NEW_OPENCDM<1
234  m_pOpencdm->ReleaseMem();
235 #endif
236  m_pOpencdm->Close();
237  SAFE_DELETE(m_pOpencdm);
238  }
239  m_eKeyState = KEY_CLOSED;
240 
241  if(m_pOutputProtection)
242  {
243  m_pOutputProtection->Release();
244  }
245 }
246 
247 
248 DrmData * AAMPOCDMSession::aampGenerateKeyRequest(string& destinationURL, uint32_t timeout)
249 {
250  DrmData * result = NULL;
251 
252  std::string challenge;
253  int challengeLength = 0;
254  pthread_mutex_lock(&decryptMutex);
255 
256 #if USE_NEW_OPENCDM
257  unsigned char temporaryUrl[1024] = {'\0'};
258  uint16_t destinationUrlLength = sizeof(temporaryUrl);
259 
260  m_pOpencdm->GetKeyMessage(challenge, temporaryUrl, destinationUrlLength);
261  if (challenge.empty() || !destinationUrlLength) {
262  m_eKeyState = KEY_ERROR;
263  AAMPLOG_WARN("aampGenerateKeyRequest :: challenge or URL is empty. ");
264  pthread_mutex_unlock(&decryptMutex);
265  return result;
266  }
267 
268 
269  std::string delimiter (":Type:");
270  std::string requestType (challenge.substr(0, challenge.find(delimiter)));
271 
272  if ( (requestType.size() != 0) && (requestType.size() != challenge.size()) ) {
273  challenge.erase(0, challenge.find(delimiter) + delimiter.length());
274  }
275 
276  result = new DrmData(reinterpret_cast<unsigned char*>(const_cast<char*>(challenge.c_str())), challenge.length());
277  destinationURL.assign(const_cast<char*>(reinterpret_cast<char*>(temporaryUrl)));
278  AAMPLOG_WARN("destination url is %s", destinationURL.c_str());
279 #else
280  unsigned char temporaryUrl[1024] = {'\0'};
281  int destinationUrlLength = 0;
282 
283  m_pOpencdm->GetKeyMessage(challenge, &challengeLength,
284  temporaryUrl, &destinationUrlLength);
285 
286  if (!challengeLength || !destinationUrlLength) {
287  m_eKeyState = KEY_ERROR;
288  AAMPLOG_WARN("aampGenerateKeyRequest :: challenge or URL is empty. ");
289  pthread_mutex_unlock(&decryptMutex);
290  return result;
291  }
292 
293  result = new DrmData(reinterpret_cast<unsigned char*>(const_cast<char*>(challenge.c_str())), challengeLength);
294  destinationURL.assign(const_cast<char*>(reinterpret_cast<char*>(temporaryUrl)));
295  AAMPLOG_WARN("destin url is %s", destinationURL.c_str());
296 #endif
297  m_eKeyState = KEY_PENDING;
298 
299  pthread_mutex_unlock(&decryptMutex);
300  return result;
301 }
302 
303 
304 int AAMPOCDMSession::aampDRMProcessKey(DrmData* key, uint32_t timeout)
305 {
306  int retvalue = -1;
307 #ifdef TRACE_LOG
308  cout << "aampDRMProcessKey :: Playready Update" << endl;
309 #endif
310  pthread_mutex_lock(&decryptMutex);
311  std::string responseMessage;
312  media::OpenCdm::KeyStatus keyStatus = media::OpenCdm::KeyStatus::InternalError;
313  std::string message = key ? key->getData():"";
314  const uint8_t* keyMessage = reinterpret_cast<const uint8_t*>(&message[0]);
315  const uint16_t keyMessageLength = key ? key->getDataLength():0;
316 
317  if (keyMessage)
318  {
319  keyStatus = m_pOpencdm->Update(keyMessage, keyMessageLength, responseMessage);
320  }
321  else
322  {
323  keyStatus = m_pOpencdm->Status();
324  }
325 
326  retvalue = (int)keyStatus;
327  if (keyStatus == media::OpenCdm::KeyStatus::Usable)
328  {
329  AAMPLOG_WARN("processKey: Key Usable!");
330  }
331  else if(keyStatus == media::OpenCdm::KeyStatus::HWError)
332  {
333  // BCOM-3537 - SAGE Hang .. Need to restart the wpecdmi process and then self kill player to recover
334  AAMPLOG_WARN("processKey: Update() returned HWError.Restarting process...");
335  int systemResult = -1;
336  // In Release another process handles opencdm which needs to be restarts .In Sprint this process is not available.
337  // So check if process exists before killing it .
338  systemResult = system("pgrep WPEcdmi");
339  if(systemResult == 0)
340  {
341  systemResult = system("pkill -9 WPEcdmi");
342  if(systemResult != 0)
343  {
344  AAMPLOG_WARN("Unable to shutdown WPEcdmi process.%d", systemResult);
345  }
346  }
347  else
348  {
349  // check for WPEFramework process
350  systemResult = system("pgrep WPEFramework");
351  if(systemResult == 0)
352  {
353  systemResult = system("pkill -9 WPEFramework");
354  if(systemResult != 0)
355  {
356  AAMPLOG_WARN("Unable to shutdown WPEFramework process.%d", systemResult);
357  }
358  }
359  }
360 
361  // wait for 5sec for all the logs to be flushed
362  sleep(5);
363  // Now kill self
364  pid_t pid = getpid();
365  syscall(__NR_tgkill, pid, pid, SIGKILL);
366  }
367  else
368  {
369  if(keyStatus == media::OpenCdm::KeyStatus::OutputRestricted)
370  {
371  AAMPLOG_WARN("processKey: Update() Output restricted keystatus: %d", (int) keyStatus);
372  retvalue = HDCP_OUTPUT_PROTECTION_FAILURE;
373  }
374  else if(keyStatus == media::OpenCdm::KeyStatus::OutputRestrictedHDCP22)
375  {
376  AAMPLOG_WARN("processKey: Update() Output Compliance error keystatus: %d", (int) keyStatus);
377  retvalue = HDCP_COMPLIANCE_CHECK_FAILURE;
378  }
379  else
380  {
381  AAMPLOG_WARN("processKey: Update() returned keystatus: %d", (int) keyStatus);
382  }
383  m_eKeyState = KEY_ERROR;
384  pthread_mutex_unlock(&decryptMutex);
385  return retvalue;
386  }
387 
388  m_eKeyState = KEY_READY;
389 #if USE_NEW_OPENCDM
390  m_pOpencdmDecrypt = new media::OpenCdm(m_sessionID);
391 #endif
392  pthread_mutex_unlock(&decryptMutex);
393  return retvalue;
394 }
395 
396 
397 int AAMPOCDMSession::decrypt(const uint8_t *f_pbIV, uint32_t f_cbIV,
398  const uint8_t *payloadData, uint32_t payloadDataSize, uint8_t **ppOpaqueData)
399 {
400 #ifdef USE_SAGE_SVP
401  struct Rpc_Secbuf_Info sb_info;
402 #endif
403  int retvalue = -1;
404  uint64_t start_decrypt_time;
405  uint64_t end_decrypt_time;
406 
407  *ppOpaqueData = NULL;
408 
409 #if USE_NEW_OPENCDM
410  if(!m_pOpencdmDecrypt)
411  {
412  AAMPLOG_WARN("m_pOpencdmDecrypt is NULL, can't decrypt yet!");
413  return -1;
414  }
415 #endif
416 
417  // Verify output protection parameters
418  // -----------------------------------
419  // Widevine output protection is currently supported without any external configuration.
420  // But the Playready output protection will be enabled based on 'enablePROutputProtection' flag which can be configured through RFC/aamp.cfg..
421  if((m_keySystem == PLAYREADY_KEY_SYSTEM_STRING && m_OutputProtectionEnabled) && m_pOutputProtection->IsSourceUHD()) {
422  // Source material is UHD
423  if(!m_pOutputProtection->isHDCPConnection2_2()) {
424  // UHD and not HDCP 2.2
425  AAMPLOG_ERR(" UHD source but not HDCP 2.2. FAILING decrypt");
426  return HDCP_COMPLIANCE_CHECK_FAILURE;
427  }
428  }
429 
430  pthread_mutex_lock(&decryptMutex);
431  start_decrypt_time = GetCurrentTimeStampInMSec();
432 #if USE_NEW_OPENCDM
433  retvalue = m_pOpencdmDecrypt->Decrypt(const_cast<unsigned char*>(payloadData), payloadDataSize, f_pbIV, f_cbIV);
434 #else
435  retvalue = m_pOpencdm->Decrypt(const_cast<unsigned char*>(payloadData), payloadDataSize, const_cast<unsigned char*>(f_pbIV), f_cbIV);
436 #endif
437  end_decrypt_time = GetCurrentTimeStampInMSec();
438  if(retvalue != 0)
439  {
440  media::OpenCdm::KeyStatus keyStatus = m_pOpencdm->Status();
441  AAMPLOG_INFO("decrypt returned : %d key status is : %d", retvalue,keyStatus);
442  if(keyStatus == media::OpenCdm::KeyStatus::OutputRestricted){
443  retvalue = HDCP_OUTPUT_PROTECTION_FAILURE;
444  }
445  else if(keyStatus == media::OpenCdm::KeyStatus::OutputRestrictedHDCP22){
446  retvalue = HDCP_COMPLIANCE_CHECK_FAILURE;
447  }
448  }
449  if(payloadDataSize > 0) {
450  LogPerformanceExt(__FUNCTION__, start_decrypt_time, end_decrypt_time, payloadDataSize);
451  }
452 
453  pthread_mutex_unlock(&decryptMutex);
454 #ifdef USE_SAGE_SVP
455  if( 0 == retvalue )
456  {
457  memcpy(&sb_info, payloadData, sizeof(Rpc_Secbuf_Info));
458  if (B_Secbuf_AllocWithToken(sb_info.size, (B_Secbuf_Type)sb_info.type, sb_info.token, (void **)ppOpaqueData))
459  {
460  AAMPLOG_ERR("[HHH] B_Secbuf_AllocWithToken() failed!");
461  }
462  }
463 #endif
464  return retvalue;
465 
466 }
467 
468 
470 {
471  return m_eKeyState;
472 }
473 
474 
476 {
477  AAMPLOG_WARN("[HHH] clearDecryptContext.");
478  pthread_mutex_lock(&decryptMutex);
479 
480 #if USE_NEW_OPENCDM < 1
481  if(m_pOpencdm) m_pOpencdm->ReleaseMem();
482 #else
483  SAFE_DELETE(m_pOpencdmDecrypt);
484 #endif
485  if(m_pOpencdm) m_pOpencdm->Close();
486  m_eKeyState = KEY_INIT;
487  pthread_mutex_unlock(&decryptMutex);
488 }
489 
AAMPOCDMSession::generateAampDRMSession
void generateAampDRMSession(const uint8_t *f_pbInitData, uint32_t f_cbInitData, std::string &customData)
Create drm session with given init data.
Definition: opencdmsession.cpp:194
AAMPOCDMSession::aampDRMProcessKey
int aampDRMProcessKey(DrmData *key, uint32_t timeout)
Updates the received key to DRM session.
Definition: opencdmsession.cpp:304
DrmData
To hold DRM key, license request etc.
Definition: AampDrmData.h:32
DecryptStats
Holds decryption profile stats.
Definition: AampOcdmGstSessionAdapter.cpp:43
AAMPOCDMSession::getState
KeyState getState()
Get the current state of DRM Session.
Definition: opencdmsession.cpp:469
KEY_ERROR_EMPTY_SESSION_ID
@ KEY_ERROR_EMPTY_SESSION_ID
Definition: AampDrmSession.h:61
AAMPOCDMSession::aampGenerateKeyRequest
DrmData * aampGenerateKeyRequest(string &destinationURL, uint32_t timeout)
Generate key request from DRM session Caller function should free the returned memory.
Definition: opencdmsession.cpp:248
AampLogManager
AampLogManager Class.
Definition: AampLogManager.h:150
KEY_PENDING
@ KEY_PENDING
Definition: AampDrmSession.h:57
AampOutputProtection::IsSourceUHD
bool IsSourceUHD()
Check if source is UHD using video decoder dimensions.
Definition: aampoutputprotection.cpp:102
DrmData::getDataLength
int getDataLength()
Getter method for dataLength.
Definition: AampDRMutils.cpp:83
AampDrmSession
Base class for DRM sessions.
Definition: AampDrmSession.h:69
AampOutputProtection::GetAampOutputProcectionInstance
static AampOutputProtection * GetAampOutputProcectionInstance()
Singleton for object creation.
Definition: aampoutputprotection.cpp:441
AampOutputProtection::isHDCPConnection2_2
bool isHDCPConnection2_2()
Get PlayRedy OP levels.
Definition: aampoutputprotection.h:230
priv_aamp.h
Private functions and types used internally by AAMP.
KEY_INIT
@ KEY_INIT
Definition: AampDrmSession.h:56
KEY_READY
@ KEY_READY
Definition: AampDrmSession.h:58
AAMPOCDMSession::clearDecryptContext
void clearDecryptContext()
Clear the current session context So that new init data can be bound.
Definition: opencdmsession.cpp:475
KeyState
KeyState
DRM session states.
Definition: AampDrmSession.h:54
opencdmsession.h
DRM Session management for Aamp.
DrmData::getData
const std::string & getData()
Getter method for data.
Definition: AampDRMutils.cpp:75
KEY_ERROR
@ KEY_ERROR
Definition: AampDrmSession.h:59
KEY_CLOSED
@ KEY_CLOSED
Definition: AampDrmSession.h:60
AAMPOCDMSession::decrypt
int decrypt(const uint8_t *f_pbIV, uint32_t f_cbIV, const uint8_t *payloadData, uint32_t payloadDataSize, uint8_t **ppOpaqueData)
Function to decrypt stream buffer.
Definition: opencdmsession.cpp:397