FreeRDP
h264_openh264.c
1 
21 #include <freerdp/config.h>
22 
23 #include <winpr/winpr.h>
24 #include <winpr/library.h>
25 #include <winpr/assert.h>
26 #include <winpr/cast.h>
27 
28 #include <freerdp/log.h>
29 #include <freerdp/codec/h264.h>
30 
31 #include <wels/codec_def.h>
32 #include <wels/codec_api.h>
33 #include <wels/codec_ver.h>
34 
35 #include "h264.h"
36 
37 typedef void (*pWelsGetCodecVersionEx)(OpenH264Version* pVersion);
38 
39 typedef long (*pWelsCreateDecoder)(ISVCDecoder** ppDecoder);
40 typedef void (*pWelsDestroyDecoder)(ISVCDecoder* pDecoder);
41 
42 typedef int (*pWelsCreateSVCEncoder)(ISVCEncoder** ppEncoder);
43 typedef void (*pWelsDestroySVCEncoder)(ISVCEncoder* pEncoder);
44 
45 typedef struct
46 {
47 #if defined(WITH_OPENH264_LOADING)
48  HMODULE lib;
49  OpenH264Version version;
50 #endif
51  pWelsGetCodecVersionEx WelsGetCodecVersionEx;
52  pWelsCreateDecoder WelsCreateDecoder;
53  pWelsDestroyDecoder WelsDestroyDecoder;
54  pWelsCreateSVCEncoder WelsCreateSVCEncoder;
55  pWelsDestroySVCEncoder WelsDestroySVCEncoder;
56  ISVCDecoder* pDecoder;
57  ISVCEncoder* pEncoder;
58  SEncParamExt EncParamExt;
59 } H264_CONTEXT_OPENH264;
60 
61 #if defined(WITH_OPENH264_LOADING)
62 static const char* openh264_library_names[] = {
63 #if defined(_WIN32)
64  "openh264.dll"
65 #elif defined(__APPLE__)
66  "libopenh264.dylib"
67 #else
68  "libopenh264.so"
69 #endif
70 };
71 #endif
72 
73 static void openh264_trace_callback(void* ctx, int level, const char* message)
74 {
75  H264_CONTEXT* h264 = ctx;
76  if (h264)
77  WLog_Print(h264->log, WLOG_TRACE, "%d - %s", level, message);
78 }
79 
80 static int openh264_decompress(H264_CONTEXT* WINPR_RESTRICT h264,
81  const BYTE* WINPR_RESTRICT pSrcData, UINT32 SrcSize)
82 {
83  DECODING_STATE state = dsInvalidArgument;
84  SBufferInfo sBufferInfo = { 0 };
85  SSysMEMBuffer* pSystemBuffer = NULL;
86  H264_CONTEXT_OPENH264* sys = NULL;
87  UINT32* iStride = NULL;
88  BYTE** pYUVData = NULL;
89 
90  WINPR_ASSERT(h264);
91  WINPR_ASSERT(pSrcData || (SrcSize == 0));
92 
93  sys = (H264_CONTEXT_OPENH264*)h264->pSystemData;
94  WINPR_ASSERT(sys);
95 
96  iStride = h264->iStride;
97  WINPR_ASSERT(iStride);
98 
99  pYUVData = h264->pYUVData;
100  WINPR_ASSERT(pYUVData);
101 
102  if (!sys->pDecoder)
103  return -2001;
104 
105  /*
106  * Decompress the image. The RDP host only seems to send I420 format.
107  */
108  pYUVData[0] = NULL;
109  pYUVData[1] = NULL;
110  pYUVData[2] = NULL;
111 
112  WINPR_ASSERT(sys->pDecoder);
113  state = (*sys->pDecoder)
114  ->DecodeFrame2(sys->pDecoder, pSrcData, WINPR_ASSERTING_INT_CAST(int, SrcSize),
115  pYUVData, &sBufferInfo);
116 
117  if (sBufferInfo.iBufferStatus != 1)
118  {
119  if (state == dsNoParamSets)
120  {
121  /* this happens on the first frame due to missing parameter sets */
122  state = (*sys->pDecoder)->DecodeFrame2(sys->pDecoder, NULL, 0, pYUVData, &sBufferInfo);
123  }
124  else if (state == dsErrorFree)
125  {
126  /* call DecodeFrame2 again to decode without delay */
127  state = (*sys->pDecoder)->DecodeFrame2(sys->pDecoder, NULL, 0, pYUVData, &sBufferInfo);
128  }
129  else
130  {
131  WLog_Print(h264->log, WLOG_WARN, "DecodeFrame2 state: 0x%04X iBufferStatus: %d", state,
132  sBufferInfo.iBufferStatus);
133  return -2002;
134  }
135  }
136 
137  if (state != dsErrorFree)
138  {
139  WLog_Print(h264->log, WLOG_WARN, "DecodeFrame2 state: 0x%02X", state);
140  return -2003;
141  }
142 
143 #if OPENH264_MAJOR >= 2
144  state = (*sys->pDecoder)->FlushFrame(sys->pDecoder, pYUVData, &sBufferInfo);
145  if (state != dsErrorFree)
146  {
147  WLog_Print(h264->log, WLOG_WARN, "FlushFrame state: 0x%02X", state);
148  return -2003;
149  }
150 #endif
151 
152  pSystemBuffer = &sBufferInfo.UsrData.sSystemBuffer;
153  iStride[0] = pSystemBuffer->iStride[0];
154  iStride[1] = pSystemBuffer->iStride[1];
155  iStride[2] = pSystemBuffer->iStride[1];
156 
157  if (sBufferInfo.iBufferStatus != 1)
158  {
159  WLog_Print(h264->log, WLOG_WARN, "DecodeFrame2 iBufferStatus: %d",
160  sBufferInfo.iBufferStatus);
161  return 0;
162  }
163 
164  if (state != dsErrorFree)
165  {
166  WLog_Print(h264->log, WLOG_WARN, "DecodeFrame2 state: 0x%02X", state);
167  return -2003;
168  }
169 
170 #if 0
171  WLog_Print(h264->log, WLOG_INFO,
172  "h264_decompress: state=%u, pYUVData=[%p,%p,%p], bufferStatus=%d, width=%d, height=%d, format=%d, stride=[%d,%d]",
173  state, (void*) pYUVData[0], (void*) pYUVData[1], (void*) pYUVData[2], sBufferInfo.iBufferStatus,
174  pSystemBuffer->iWidth, pSystemBuffer->iHeight, pSystemBuffer->iFormat,
175  pSystemBuffer->iStride[0], pSystemBuffer->iStride[1]);
176 #endif
177 
178  if (pSystemBuffer->iFormat != videoFormatI420)
179  return -2004;
180 
181  if (!pYUVData[0] || !pYUVData[1] || !pYUVData[2])
182  return -2005;
183 
184  return 1;
185 }
186 
187 static int openh264_compress(H264_CONTEXT* WINPR_RESTRICT h264,
188  const BYTE** WINPR_RESTRICT pYUVData,
189  const UINT32* WINPR_RESTRICT iStride, BYTE** WINPR_RESTRICT ppDstData,
190  UINT32* WINPR_RESTRICT pDstSize)
191 {
192  int status = 0;
193  SFrameBSInfo info = { 0 };
194  SSourcePicture pic = { 0 };
195 
196  H264_CONTEXT_OPENH264* sys = NULL;
197 
198  WINPR_ASSERT(h264);
199  WINPR_ASSERT(pYUVData);
200  WINPR_ASSERT(iStride);
201  WINPR_ASSERT(ppDstData);
202  WINPR_ASSERT(pDstSize);
203 
204  sys = &((H264_CONTEXT_OPENH264*)h264->pSystemData)[0];
205  WINPR_ASSERT(sys);
206 
207  if (!sys->pEncoder)
208  return -1;
209 
210  if (!pYUVData[0] || !pYUVData[1] || !pYUVData[2])
211  return -1;
212 
213  if ((h264->width > INT_MAX) || (h264->height > INT_MAX))
214  return -1;
215 
216  if ((h264->FrameRate > INT_MAX) || (h264->NumberOfThreads > INT_MAX) ||
217  (h264->BitRate > INT_MAX) || (h264->QP > INT_MAX))
218  return -1;
219 
220  WINPR_ASSERT(sys->pEncoder);
221  if ((sys->EncParamExt.iPicWidth != (int)h264->width) ||
222  (sys->EncParamExt.iPicHeight != (int)h264->height))
223  {
224  WINPR_ASSERT((*sys->pEncoder)->GetDefaultParams);
225  status = (*sys->pEncoder)->GetDefaultParams(sys->pEncoder, &sys->EncParamExt);
226 
227  if (status < 0)
228  {
229  WLog_Print(h264->log, WLOG_ERROR,
230  "Failed to get OpenH264 default parameters (status=%d)", status);
231  return status;
232  }
233 
234  EUsageType usageType = SCREEN_CONTENT_REAL_TIME;
235 
236  switch (h264->UsageType)
237  {
238  case H264_CAMERA_VIDEO_NON_REAL_TIME:
239  usageType = CAMERA_VIDEO_NON_REAL_TIME;
240  break;
241  case H264_CAMERA_VIDEO_REAL_TIME:
242  usageType = CAMERA_VIDEO_REAL_TIME;
243  break;
244  case H264_SCREEN_CONTENT_NON_REAL_TIME:
245  usageType = SCREEN_CONTENT_NON_REAL_TIME;
246  break;
247  case H264_SCREEN_CONTENT_REAL_TIME:
248  default:
249  break;
250  }
251 
252  sys->EncParamExt.iUsageType = usageType;
253  sys->EncParamExt.iPicWidth = WINPR_ASSERTING_INT_CAST(int, h264->width);
254  sys->EncParamExt.iPicHeight = WINPR_ASSERTING_INT_CAST(int, h264->height);
255  sys->EncParamExt.fMaxFrameRate = WINPR_ASSERTING_INT_CAST(int, h264->FrameRate);
256  sys->EncParamExt.iMaxBitrate = UNSPECIFIED_BIT_RATE;
257  sys->EncParamExt.bEnableDenoise = 0;
258  sys->EncParamExt.bEnableLongTermReference = 0;
259  sys->EncParamExt.iSpatialLayerNum = 1;
260  sys->EncParamExt.iMultipleThreadIdc = (int)h264->NumberOfThreads;
261  sys->EncParamExt.sSpatialLayers[0].fFrameRate = h264->FrameRate;
262  sys->EncParamExt.sSpatialLayers[0].iVideoWidth = sys->EncParamExt.iPicWidth;
263  sys->EncParamExt.sSpatialLayers[0].iVideoHeight = sys->EncParamExt.iPicHeight;
264  sys->EncParamExt.sSpatialLayers[0].iMaxSpatialBitrate = sys->EncParamExt.iMaxBitrate;
265 
266  switch (h264->RateControlMode)
267  {
268  case H264_RATECONTROL_VBR:
269  sys->EncParamExt.iRCMode = RC_BITRATE_MODE;
270  sys->EncParamExt.iTargetBitrate = (int)h264->BitRate;
271  sys->EncParamExt.sSpatialLayers[0].iSpatialBitrate =
272  sys->EncParamExt.iTargetBitrate;
273  sys->EncParamExt.bEnableFrameSkip = 1;
274  break;
275 
276  case H264_RATECONTROL_CQP:
277  sys->EncParamExt.iRCMode = RC_OFF_MODE;
278  sys->EncParamExt.sSpatialLayers[0].iDLayerQp = (int)h264->QP;
279  sys->EncParamExt.bEnableFrameSkip = 0;
280  break;
281  }
282 
283  if (sys->EncParamExt.iMultipleThreadIdc > 1)
284  {
285 #if (OPENH264_MAJOR == 1) && (OPENH264_MINOR <= 5)
286  sys->EncParamExt.sSpatialLayers[0].sSliceCfg.uiSliceMode = SM_AUTO_SLICE;
287 #else
288  sys->EncParamExt.sSpatialLayers[0].sSliceArgument.uiSliceMode = SM_FIXEDSLCNUM_SLICE;
289 #endif
290  }
291 
292  WINPR_ASSERT((*sys->pEncoder)->InitializeExt);
293  status = (*sys->pEncoder)->InitializeExt(sys->pEncoder, &sys->EncParamExt);
294 
295  if (status < 0)
296  {
297  WLog_Print(h264->log, WLOG_ERROR, "Failed to initialize OpenH264 encoder (status=%d)",
298  status);
299  return status;
300  }
301 
302  WINPR_ASSERT((*sys->pEncoder)->GetOption);
303  status =
304  (*sys->pEncoder)
305  ->GetOption(sys->pEncoder, ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, &sys->EncParamExt);
306 
307  if (status < 0)
308  {
309  WLog_Print(h264->log, WLOG_ERROR,
310  "Failed to get initial OpenH264 encoder parameters (status=%d)", status);
311  return status;
312  }
313  }
314  else
315  {
316  switch (h264->RateControlMode)
317  {
318  case H264_RATECONTROL_VBR:
319  if (sys->EncParamExt.iTargetBitrate != (int)h264->BitRate)
320  {
321  SBitrateInfo bitrate = { 0 };
322 
323  sys->EncParamExt.iTargetBitrate = (int)h264->BitRate;
324  bitrate.iLayer = SPATIAL_LAYER_ALL;
325  bitrate.iBitrate = (int)h264->BitRate;
326 
327  WINPR_ASSERT((*sys->pEncoder)->SetOption);
328  status = (*sys->pEncoder)
329  ->SetOption(sys->pEncoder, ENCODER_OPTION_BITRATE, &bitrate);
330 
331  if (status < 0)
332  {
333  WLog_Print(h264->log, WLOG_ERROR,
334  "Failed to set encoder bitrate (status=%d)", status);
335  return status;
336  }
337  }
338 
339  if (sys->EncParamExt.fMaxFrameRate != (int)h264->FrameRate)
340  {
341  sys->EncParamExt.fMaxFrameRate = (int)h264->FrameRate;
342 
343  WINPR_ASSERT((*sys->pEncoder)->SetOption);
344  status = (*sys->pEncoder)
345  ->SetOption(sys->pEncoder, ENCODER_OPTION_FRAME_RATE,
346  &sys->EncParamExt.fMaxFrameRate);
347 
348  if (status < 0)
349  {
350  WLog_Print(h264->log, WLOG_ERROR,
351  "Failed to set encoder framerate (status=%d)", status);
352  return status;
353  }
354  }
355 
356  break;
357 
358  case H264_RATECONTROL_CQP:
359  if (sys->EncParamExt.sSpatialLayers[0].iDLayerQp != (int)h264->QP)
360  {
361  sys->EncParamExt.sSpatialLayers[0].iDLayerQp = (int)h264->QP;
362 
363  WINPR_ASSERT((*sys->pEncoder)->SetOption);
364  status = (*sys->pEncoder)
365  ->SetOption(sys->pEncoder, ENCODER_OPTION_SVC_ENCODE_PARAM_EXT,
366  &sys->EncParamExt);
367 
368  if (status < 0)
369  {
370  WLog_Print(h264->log, WLOG_ERROR,
371  "Failed to set encoder parameters (status=%d)", status);
372  return status;
373  }
374  }
375 
376  break;
377  }
378  }
379 
380  pic.iPicWidth = (int)h264->width;
381  pic.iPicHeight = (int)h264->height;
382  pic.iColorFormat = videoFormatI420;
383  pic.iStride[0] = (int)iStride[0];
384  pic.iStride[1] = (int)iStride[1];
385  pic.iStride[2] = (int)iStride[2];
386  pic.pData[0] = WINPR_CAST_CONST_PTR_AWAY(pYUVData[0], BYTE*);
387  pic.pData[1] = WINPR_CAST_CONST_PTR_AWAY(pYUVData[1], BYTE*);
388  pic.pData[2] = WINPR_CAST_CONST_PTR_AWAY(pYUVData[2], BYTE*);
389 
390  WINPR_ASSERT((*sys->pEncoder)->EncodeFrame);
391  status = (*sys->pEncoder)->EncodeFrame(sys->pEncoder, &pic, &info);
392 
393  if (status < 0)
394  {
395  WLog_Print(h264->log, WLOG_ERROR, "Failed to encode frame (status=%d)", status);
396  return status;
397  }
398 
399  *ppDstData = info.sLayerInfo[0].pBsBuf;
400  *pDstSize = 0;
401 
402  for (int i = 0; i < info.iLayerNum; i++)
403  {
404  for (int j = 0; j < info.sLayerInfo[i].iNalCount; j++)
405  {
406  *pDstSize += info.sLayerInfo[i].pNalLengthInByte[j];
407  }
408  }
409 
410  return 1;
411 }
412 
413 static void openh264_uninit(H264_CONTEXT* h264)
414 {
415  H264_CONTEXT_OPENH264* sysContexts = NULL;
416 
417  WINPR_ASSERT(h264);
418 
419  sysContexts = (H264_CONTEXT_OPENH264*)h264->pSystemData;
420 
421  if (sysContexts)
422  {
423  for (UINT32 x = 0; x < h264->numSystemData; x++)
424  {
425  H264_CONTEXT_OPENH264* sys = &sysContexts[x];
426 
427  if (sys->pDecoder)
428  {
429  (*sys->pDecoder)->Uninitialize(sys->pDecoder);
430  sysContexts->WelsDestroyDecoder(sys->pDecoder);
431  sys->pDecoder = NULL;
432  }
433 
434  if (sys->pEncoder)
435  {
436  (*sys->pEncoder)->Uninitialize(sys->pEncoder);
437  sysContexts->WelsDestroySVCEncoder(sys->pEncoder);
438  sys->pEncoder = NULL;
439  }
440  }
441 
442 #if defined(WITH_OPENH264_LOADING)
443  if (sysContexts->lib)
444  FreeLibrary(sysContexts->lib);
445 #endif
446  free(h264->pSystemData);
447  h264->pSystemData = NULL;
448  }
449 }
450 
451 #if defined(WITH_OPENH264_LOADING)
452 static BOOL openh264_load_functionpointers(H264_CONTEXT* h264, const char* name)
453 {
454  H264_CONTEXT_OPENH264* sysContexts;
455 
456  WINPR_ASSERT(name);
457 
458  if (!h264)
459  return FALSE;
460 
461  sysContexts = h264->pSystemData;
462 
463  if (!sysContexts)
464  return FALSE;
465 
466  sysContexts->lib = LoadLibraryA(name);
467 
468  if (!sysContexts->lib)
469  return FALSE;
470 
471  sysContexts->WelsGetCodecVersionEx =
472  GetProcAddressAs(sysContexts->lib, "WelsGetCodecVersionEx", pWelsGetCodecVersionEx);
473  sysContexts->WelsCreateDecoder =
474  GetProcAddressAs(sysContexts->lib, "WelsCreateDecoder", pWelsCreateDecoder);
475  sysContexts->WelsDestroyDecoder =
476  GetProcAddressAs(sysContexts->lib, "WelsDestroyDecoder", pWelsDestroyDecoder);
477  sysContexts->WelsCreateSVCEncoder =
478  GetProcAddressAs(sysContexts->lib, "WelsCreateSVCEncoder", pWelsCreateSVCEncoder);
479  sysContexts->WelsDestroySVCEncoder =
480  GetProcAddressAs(sysContexts->lib, "WelsDestroySVCEncoder", pWelsDestroySVCEncoder);
481 
482  if (!sysContexts->WelsCreateDecoder || !sysContexts->WelsDestroyDecoder ||
483  !sysContexts->WelsCreateSVCEncoder || !sysContexts->WelsDestroySVCEncoder ||
484  !sysContexts->WelsGetCodecVersionEx)
485  {
486  FreeLibrary(sysContexts->lib);
487  sysContexts->lib = NULL;
488  return FALSE;
489  }
490 
491  sysContexts->WelsGetCodecVersionEx(&sysContexts->version);
492  WLog_Print(h264->log, WLOG_INFO, "loaded %s %d.%d.%d", name, sysContexts->version.uMajor,
493  sysContexts->version.uMinor, sysContexts->version.uRevision);
494 
495  if ((sysContexts->version.uMajor < 1) ||
496  ((sysContexts->version.uMajor == 1) && (sysContexts->version.uMinor < 6)))
497  {
498  WLog_Print(
499  h264->log, WLOG_ERROR,
500  "OpenH264 %s %d.%d.%d is too old, need at least version 1.6.0 for dynamic loading",
501  name, sysContexts->version.uMajor, sysContexts->version.uMinor,
502  sysContexts->version.uRevision);
503  FreeLibrary(sysContexts->lib);
504  sysContexts->lib = NULL;
505  return FALSE;
506  }
507 
508  return TRUE;
509 }
510 #endif
511 
512 static BOOL openh264_init(H264_CONTEXT* h264)
513 {
514 #if defined(WITH_OPENH264_LOADING)
515  BOOL success = FALSE;
516 #endif
517  long status = 0;
518  H264_CONTEXT_OPENH264* sysContexts = NULL;
519  static int traceLevel = WELS_LOG_DEBUG;
520 #if (OPENH264_MAJOR == 1) && (OPENH264_MINOR <= 5)
521  static EVideoFormatType videoFormat = videoFormatI420;
522 #endif
523  static WelsTraceCallback traceCallback = openh264_trace_callback;
524 
525  WINPR_ASSERT(h264);
526 
527  h264->numSystemData = 1;
528  sysContexts =
529  (H264_CONTEXT_OPENH264*)calloc(h264->numSystemData, sizeof(H264_CONTEXT_OPENH264));
530 
531  if (!sysContexts)
532  goto EXCEPTION;
533 
534  h264->pSystemData = (void*)sysContexts;
535 #if defined(WITH_OPENH264_LOADING)
536 
537  for (size_t i = 0; i < ARRAYSIZE(openh264_library_names); i++)
538  {
539  const char* current = openh264_library_names[i];
540  success = openh264_load_functionpointers(h264, current);
541 
542  if (success)
543  break;
544  }
545 
546  if (!success)
547  goto EXCEPTION;
548 
549 #else
550  sysContexts->WelsGetCodecVersionEx = WelsGetCodecVersionEx;
551  sysContexts->WelsCreateDecoder = WelsCreateDecoder;
552  sysContexts->WelsDestroyDecoder = WelsDestroyDecoder;
553  sysContexts->WelsCreateSVCEncoder = WelsCreateSVCEncoder;
554  sysContexts->WelsDestroySVCEncoder = WelsDestroySVCEncoder;
555 #endif
556 
557  for (UINT32 x = 0; x < h264->numSystemData; x++)
558  {
559  SDecodingParam sDecParam = { 0 };
560  H264_CONTEXT_OPENH264* sys = &sysContexts[x];
561 
562  if (h264->Compressor)
563  {
564  sysContexts->WelsCreateSVCEncoder(&sys->pEncoder);
565 
566  if (!sys->pEncoder)
567  {
568  WLog_Print(h264->log, WLOG_ERROR, "Failed to create OpenH264 encoder");
569  goto EXCEPTION;
570  }
571  }
572  else
573  {
574  sysContexts->WelsCreateDecoder(&sys->pDecoder);
575 
576  if (!sys->pDecoder)
577  {
578  WLog_Print(h264->log, WLOG_ERROR, "Failed to create OpenH264 decoder");
579  goto EXCEPTION;
580  }
581 
582 #if (OPENH264_MAJOR == 1) && (OPENH264_MINOR <= 5)
583  sDecParam.eOutputColorFormat = videoFormatI420;
584 #endif
585  sDecParam.eEcActiveIdc = ERROR_CON_FRAME_COPY;
586  sDecParam.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_AVC;
587  status = (*sys->pDecoder)->Initialize(sys->pDecoder, &sDecParam);
588 
589  if (status != 0)
590  {
591  WLog_Print(h264->log, WLOG_ERROR,
592  "Failed to initialize OpenH264 decoder (status=%ld)", status);
593  goto EXCEPTION;
594  }
595 
596 #if (OPENH264_MAJOR == 1) && (OPENH264_MINOR <= 5)
597  status =
598  (*sys->pDecoder)->SetOption(sys->pDecoder, DECODER_OPTION_DATAFORMAT, &videoFormat);
599 #endif
600 
601  if (status != 0)
602  {
603  WLog_Print(h264->log, WLOG_ERROR,
604  "Failed to set data format option on OpenH264 decoder (status=%ld)",
605  status);
606  goto EXCEPTION;
607  }
608 
609  if (WLog_GetLogLevel(h264->log) == WLOG_TRACE)
610  {
611  status = (*sys->pDecoder)
612  ->SetOption(sys->pDecoder, DECODER_OPTION_TRACE_LEVEL, &traceLevel);
613 
614  if (status != 0)
615  {
616  WLog_Print(h264->log, WLOG_ERROR,
617  "Failed to set trace level option on OpenH264 decoder (status=%ld)",
618  status);
619  goto EXCEPTION;
620  }
621 
622  status =
623  (*sys->pDecoder)
624  ->SetOption(sys->pDecoder, DECODER_OPTION_TRACE_CALLBACK_CONTEXT, h264);
625 
626  if (status != 0)
627  {
628  WLog_Print(h264->log, WLOG_ERROR,
629  "Failed to set trace callback context option on OpenH264 decoder "
630  "(status=%ld)",
631  status);
632  goto EXCEPTION;
633  }
634 
635  status =
636  (*sys->pDecoder)
637  ->SetOption(sys->pDecoder, DECODER_OPTION_TRACE_CALLBACK, traceCallback);
638 
639  if (status != 0)
640  {
641  WLog_Print(
642  h264->log, WLOG_ERROR,
643  "Failed to set trace callback option on OpenH264 decoder (status=%ld)",
644  status);
645  goto EXCEPTION;
646  }
647  }
648  }
649  }
650 
651  return TRUE;
652 EXCEPTION:
653  openh264_uninit(h264);
654  return FALSE;
655 }
656 
657 const H264_CONTEXT_SUBSYSTEM g_Subsystem_OpenH264 = { "OpenH264", openh264_init, openh264_uninit,
658  openh264_decompress, openh264_compress };