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