RDK Documentation (Open Sourced RDK Components)
AampOcdmGstSessionAdapter.cpp
Go to the documentation of this file.
1 /**
2  * @file AampOcdmGstSessionAdapter.cpp
3  * @brief File holds operations on OCDM gst sessions
4  */
5 
6 #include <sys/time.h>
8 
9 #ifdef AMLOGIC
10 #include "gst/video/gstvideotimecode.h"
11 #include "gst/video/gstvideometa.h"
12 #endif
13 
14 #define USEC_PER_SEC 1000000
15 static inline uint64_t GetCurrentTimeStampInUSec()
16 {
17  struct timeval timeStamp;
18  uint64_t retVal = 0;
19 
20  gettimeofday(&timeStamp, NULL);
21 
22  // Convert timestamp to Micro Seconds
23  retVal = (uint64_t)(((uint64_t) timeStamp.tv_sec * USEC_PER_SEC) + timeStamp.tv_usec);
24 
25  return retVal;
26 }
27 
28 static inline uint64_t GetCurrentTimeStampInMSec()
29 {
30  return GetCurrentTimeStampInUSec() / 1000;
31 }
32 
33 #define LOG_DECRYPT_STATS 1
34 #define DECRYPT_AVG_TIME_THRESHOLD 10.0 //10 milliseconds
35 #ifdef LOG_DECRYPT_STATS
36 #define MAX_THREADS 10
37 #define INTERVAL 120
38 
39 /**
40  * @struct DecryptStats
41  * @brief Holds decryption profile stats
42  */
44 {
45  uint64_t nBytesInterval;
46  uint64_t nTimeInterval;
47  uint64_t nBytesTotal;
48  uint64_t nTimeTotal;
49  uint64_t nCallsTotal;
50  pthread_t threadID;
51 
52 };
53 #endif // LOG_DECRYPT_STATS
54 #define SEC_SIZE size_t
55 void LogPerformanceExt(const char *strFunc, uint64_t msStart, uint64_t msEnd, SEC_SIZE nDataSize)
56 {
57  uint64_t delta = msEnd - msStart;
58  //CID: 107327,26,25,24,23 - Removed the unused initialized variables : bThreshold,nDataMin,nRestart,nRateMin,nTimeMin
59 
60 #ifdef LOG_DECRYPT_STATS
61  {
62  static DecryptStats stats[MAX_THREADS] = { 0 };
63  int idx = 0;
64  while (idx < MAX_THREADS)
65  {
66  if (stats[idx].threadID == pthread_self())
67  {
68  break;
69  }
70  idx++;
71  }
72  if (idx == MAX_THREADS)
73  {
74  // new thread
75  idx = 0;
76  while (idx < MAX_THREADS)
77  {
78  if (stats[idx].threadID == 0)
79  {
80  // empty slot
81  stats[idx].threadID = pthread_self();
82  break;
83  }
84  idx++;
85  }
86  }
87  if (idx == MAX_THREADS)
88  {
89  printf("%s >>>>>>>> All slots allocated!!!, idx = %d, clearing the array.\n", __FUNCTION__, idx);
90  memset(stats, 0, sizeof(DecryptStats) * MAX_THREADS);
91  return;
92  }
93 
94  if (nDataSize > 0)
95  {
96  stats[idx].nBytesInterval += (uint64_t) nDataSize;
97  stats[idx].nTimeInterval += delta;
98  stats[idx].nCallsTotal++;
99 
100  if (stats[idx].nCallsTotal % INTERVAL == 0)
101  {
102  stats[idx].nBytesTotal += stats[idx].nBytesInterval;
103  stats[idx].nTimeTotal += stats[idx].nTimeInterval;
104  double avgTime = (double) stats[idx].nTimeTotal / (double) stats[idx].nCallsTotal;
105  if (avgTime >= DECRYPT_AVG_TIME_THRESHOLD)
106  {
107  logprintf("%s >>>>>>>> Thread ID %X (%d) Avg Time %0.2llf ms, Avg Bytes %llu calls (%llu) Interval avg time %0.2llf, Interval avg bytes %llu",
108  strFunc, stats[idx].threadID, idx, avgTime, stats[idx].nBytesTotal / stats[idx].nCallsTotal, stats[idx].nCallsTotal, (double) stats[idx].nTimeInterval / (double) INTERVAL, stats[idx].nBytesInterval / INTERVAL);
109  }
110  stats[idx].nBytesInterval = 0;
111  stats[idx].nTimeInterval = 0;
112 
113  }
114  }
115  }
116 #endif //LOG_DECRYPT_STATS
117 }
118 
119 #ifdef AMLOGIC
120 class BitStreamState
121 {
122  private:
123  const guint8 *ptr;
124  gsize bit_offset;
125  public:
126  BitStreamState(const BitStreamState &L):
127  ptr(L.ptr),
128  bit_offset(L.bit_offset) {} // copy constructor
129  BitStreamState & operator=(const BitStreamState &L)
130  {
131  ptr = L.ptr;
132  bit_offset = L.bit_offset;
133  return *this;
134  } // assignment
135  BitStreamState(const guint8 *ptr ):
136  ptr(ptr),
137  bit_offset(0)
138  {
139  }
140  ~BitStreamState()
141  {
142  this->ptr = NULL;
143  this->bit_offset = 0;
144  }
145 
146  int Read( int bit_count = 1 )
147  {
148  int rc = 0;
149  while( bit_count-- && ptr)
150  {
151  rc <<= 1;
152  int mask = 0x80>>(bit_offset&0x7);
153  if( ptr[bit_offset/8] & mask )
154  {
155  rc |= 1;
156  }
157  bit_offset++;
158  }
159  return rc;
160  }
161 };
162 
163 /**
164  * @brief Extract SEI
165  */
166 void AAMPOCDMGSTSessionAdapter::ExtractSEI( GstBuffer *buffer)
167 {
168  GstMapInfo info;
169  const guint8 *ptr;
170  gsize len;
171  if (buffer)
172  {
173  gst_buffer_map( buffer, &info, (GstMapFlags)(GST_MAP_READ) );
174  ptr = info.data;
175  len = info.size;
176  }
177  else
178  {
179  logprintf("Invalid Buffer Input - NULL");
180  gst_buffer_unmap(buffer, &info);
181  return;
182  }
183 
184  if (len > 2048)
185  len = 2048;
186 
187  for( int i=0; i<len-4; i++ )
188  {
189  if( ptr[i+0] == 0x00 &&
190  ptr[i+1] == 0x00 &&
191  ptr[i+2] == 0x00 &&
192  ptr[i+3] == 0x0b )
193  { // brute force for now
194  BitStreamState bitstream(&ptr[i+4]);
195  int forbidden_zero_bit = bitstream.Read();
196  int NALUnitTpe = bitstream.Read(6);
197  if( NALUnitTpe == 39 ) // NAL_SEI_PREFIX
198  {
199  int nuh_layer_id = bitstream.Read(6);
200  int nuh_temporal_id_plus1 = bitstream.Read(3);
201  int payload_type = bitstream.Read(8); // 0x88 (136)
202  if( payload_type == 136 ) // SeiMessage::TIME_CODE
203  {
204  int payload_size = bitstream.Read(8);
205  int num_clock_ts = bitstream.Read(2 );
206  for( int j=0; j<num_clock_ts; j++ )
207  {
208  int clock_time_stamp_flag = bitstream.Read(1 );
209  if( clock_time_stamp_flag )
210  {
211  int nuit_field_based_flag = bitstream.Read();
212  int counting_type = bitstream.Read(5);
213  int full_timestamp_flag = bitstream.Read();
214  int discontinuity_flag = bitstream.Read();
215  int cnt_dropped_flag = bitstream.Read();
216  int n_frames = bitstream.Read(9);
217  int seconds_value = 0;
218  int minutes_value = 0;
219  int hours_value = 0;
220  if(full_timestamp_flag )
221  {
222  seconds_value = bitstream.Read(6);
223  minutes_value = bitstream.Read(6);
224  hours_value = bitstream.Read(5);
225  }
226  else
227  {
228  int seconds_flag = bitstream.Read();
229  if( seconds_flag )
230  {
231  seconds_value = bitstream.Read(6);
232  int minutes_flag = bitstream.Read();
233  if( minutes_flag)
234  {
235  minutes_value = bitstream.Read(6);
236  int hours_flag = bitstream.Read();
237  if( hours_flag )
238  {
239  hours_value = bitstream.Read(5);
240  }
241  }
242  }
243  }
244  AAMPLOG_TRACE( "SEI (HH:MM:SS) %02d:%02d:%02d number of frames (%d)", hours_value, minutes_value, seconds_value, n_frames );
245  gst_buffer_add_video_time_code_meta_full(
246  buffer,
247  0, // fps_n
248  1, // fps_d
249  NULL, // latest_daily_jam
250  GST_VIDEO_TIME_CODE_FLAGS_NONE,
251  hours_value,
252  minutes_value,
253  seconds_value,
254  n_frames,
255  0 // field_count
256  );
257  break;
258  }
259  }
260  }
261  }
262  }
263  }
264 
265  /** Unmap buffer after use **/
266  gst_buffer_unmap(buffer, &info);
267 }
268 #endif
269 
270 /**
271  * @brief decrypt the data
272  */
273 int AAMPOCDMGSTSessionAdapter::decrypt(GstBuffer *keyIDBuffer, GstBuffer *ivBuffer, GstBuffer *buffer, unsigned subSampleCount, GstBuffer *subSamplesBuffer, GstCaps* caps)
274 {
275  int retValue = -1;
276 
277  if (m_pOpenCDMSession)
278  {
279  uint64_t start_decrypt_time;
280  uint64_t end_decrypt_time;
281 
282  if (!verifyOutputProtection())
283  {
284  return HDCP_COMPLIANCE_CHECK_FAILURE;
285  }
286 
287 #ifdef AMLOGIC
288  /**
289  * Extract the SEI timestamps from both clear and encrypted content.
290  */
291  AAMPLOG_TRACE("DEBUG: Extract the SEI timestamps from encrypted content.");
292  ExtractSEI(buffer);
293 #endif
294  pthread_mutex_lock(&decryptMutex);
295  start_decrypt_time = GetCurrentTimeStampInMSec();
296 
297 #if defined(AMLOGIC)
298  /* Added GST_IS_CAPS check also before passing gst caps to OCDM decrypt() as gst_caps_is_empty returns false when caps object is not of
299  type GST_TYPE_CAPS. This will avoid crash when caps is not of type GST_TYPE_CAPS. */
300  if (AAMPOCDMGSTSessionDecrypt && !gst_caps_is_empty(caps) && GST_IS_CAPS(caps))
301  {
302  AAMPLOG_TRACE("Caps is %s", gst_caps_to_string(caps));
303  retValue = AAMPOCDMGSTSessionDecrypt(m_pOpenCDMSession, buffer, subSamplesBuffer, subSampleCount, ivBuffer, keyIDBuffer, 0, caps);
304  }
305  else
306 #endif
307  retValue = opencdm_gstreamer_session_decrypt(m_pOpenCDMSession, buffer, subSamplesBuffer, subSampleCount, ivBuffer, keyIDBuffer, 0);
308  end_decrypt_time = GetCurrentTimeStampInMSec();
309  if (retValue != 0)
310  {
311  GstMapInfo keyIDMap;
312  if (gst_buffer_map(keyIDBuffer, &keyIDMap, (GstMapFlags) GST_MAP_READ) == true)
313  {
314  uint8_t *mappedKeyID = reinterpret_cast<uint8_t*>(keyIDMap.data);
315  uint32_t mappedKeyIDSize = static_cast<uint32_t>(keyIDMap.size);
316 #ifdef USE_THUNDER_OCDM_API_0_2
317  KeyStatus keyStatus = opencdm_session_status(m_pOpenCDMSession, mappedKeyID, mappedKeyIDSize);
318 #else
319  KeyStatus keyStatus = opencdm_session_status(m_pOpenCDMSession, mappedKeyID,mappedKeyIDSize );
320 #endif
321  AAMPLOG_INFO("AAMPOCDMSessionAdapter: decrypt returned : %d key status is : %d", retValue, keyStatus);
322 #ifdef USE_THUNDER_OCDM_API_0_2
323  if (keyStatus == OutputRestricted){
324 #else
325  if(keyStatus == KeyStatus::OutputRestricted){
326 #endif
327  retValue = HDCP_OUTPUT_PROTECTION_FAILURE;
328  }
329 #ifdef USE_THUNDER_OCDM_API_0_2
330  else if (keyStatus == OutputRestrictedHDCP22){
331 #else
332  else if(keyStatus == KeyStatus::OutputRestrictedHDCP22){
333 #endif
334  retValue = HDCP_COMPLIANCE_CHECK_FAILURE;
335  }
336  gst_buffer_unmap(keyIDBuffer, &keyIDMap);
337  }
338  }
339 
340  GstMapInfo mapInfo;
341  if (gst_buffer_map(buffer, &mapInfo, GST_MAP_READ))
342  {
343  if (mapInfo.size > 0)
344  {
345  LogPerformanceExt(__FUNCTION__, start_decrypt_time, end_decrypt_time, mapInfo.size);
346  }
347  gst_buffer_unmap(buffer, &mapInfo);
348  }
349 
350  pthread_mutex_unlock(&decryptMutex);
351  }
352  return retValue;
353 }
354 
355 int AAMPOCDMGSTSessionAdapter::decrypt(const uint8_t *f_pbIV, uint32_t f_cbIV, const uint8_t *payloadData, uint32_t payloadDataSize, uint8_t **ppOpaqueData)
356 {
357  int retValue = -1;
358 
359  if (m_pOpenCDMSession)
360  {
361  uint64_t start_decrypt_time;
362  uint64_t end_decrypt_time;
363 
364  if (!verifyOutputProtection())
365  {
366  return HDCP_COMPLIANCE_CHECK_FAILURE;
367  }
368 
369  pthread_mutex_lock(&decryptMutex);
370  start_decrypt_time = GetCurrentTimeStampInMSec();
371  retValue = opencdm_session_decrypt(m_pOpenCDMSession, (uint8_t *)payloadData, payloadDataSize, f_pbIV, f_cbIV, NULL, 0, 0);
372  end_decrypt_time = GetCurrentTimeStampInMSec();
373  if (retValue != 0)
374  {
375 #ifdef USE_THUNDER_OCDM_API_0_2
376  KeyStatus keyStatus = opencdm_session_status(m_pOpenCDMSession, NULL, 0);
377 #else
378  KeyStatus keyStatus = opencdm_session_status(m_pOpenCDMSession, NULL, 0);
379 #endif
380  AAMPLOG_INFO("AAMPOCDMSessionAdapter:%s : decrypt returned : %d key status is : %d", __FUNCTION__, retValue, keyStatus);
381 #ifdef USE_THUNDER_OCDM_API_0_2
382  if (keyStatus == OutputRestricted){
383 #else
384  if(keyStatus == KeyStatus::OutputRestricted){
385 #endif
386  retValue = HDCP_OUTPUT_PROTECTION_FAILURE;
387  }
388 #ifdef USE_THUNDER_OCDM_API_0_2
389  else if (keyStatus == OutputRestrictedHDCP22){
390 #else
391  else if(keyStatus == KeyStatus::OutputRestrictedHDCP22){
392 #endif
393  retValue = HDCP_COMPLIANCE_CHECK_FAILURE;
394  }
395  }
396 
397  pthread_mutex_unlock(&decryptMutex);
398  }
399  return retValue;
400 }
401 
AAMPOCDMGSTSessionAdapter::decrypt
int decrypt(GstBuffer *keyIDBuffer, GstBuffer *ivBuffer, GstBuffer *buffer, unsigned subSampleCount, GstBuffer *subSamplesBuffer, GstCaps *caps)
decrypt the data
Definition: AampOcdmGstSessionAdapter.cpp:273
DecryptStats
Holds decryption profile stats.
Definition: AampOcdmGstSessionAdapter.cpp:43
logprintf
void logprintf(const char *format,...)
Print logs to console / log fil.
Definition: aamplogging.cpp:432
AampOcdmGstSessionAdapter.h
File holds operations on OCDM gst sessions.
AAMPLOG_TRACE
#define AAMPLOG_TRACE(FORMAT,...)
AAMP logging defines, this can be enabled through setLogLevel() as per the need.
Definition: AampLogManager.h:83