FreeRDP
Loading...
Searching...
No Matches
codec/video.c
1
20#include <freerdp/config.h>
21
22#include <winpr/assert.h>
23#include <winpr/wlog.h>
24#include <winpr/stream.h>
25#include <freerdp/log.h>
26
27#include <freerdp/codec/video.h>
28#include <freerdp/codec/h264.h>
29
30#define TAG FREERDP_TAG("codec.video")
31
32#if defined(WITH_SWSCALE)
33
34/* Forward declarations for static functions */
35static BOOL freerdp_video_fill_plane_info(BYTE* data[4], int lineSize[4],
36 FREERDP_VIDEO_FORMAT format, UINT32 width, UINT32 height,
37 const BYTE* buffer);
38
39#include "image_ffmpeg.h"
40
41/* MJPEG decoder only available when NOT using runtime loading */
42#if defined(WITH_VIDEO_FFMPEG) && !defined(WITH_SWSCALE_LOADING)
43#define WITH_MJPEG_DECODER
44#include <libavcodec/avcodec.h>
45#endif
46
47struct s_FREERDP_VIDEO_CONTEXT
48{
49 UINT32 width;
50 UINT32 height;
51 struct SwsContext* sws;
52
53#if defined(WITH_MJPEG_DECODER)
54 AVCodecContext* mjpegDecoder;
55 AVPacket* mjpegPacket;
56 AVFrame* mjpegFrame;
57#endif
58
59 H264_CONTEXT* h264;
60 BOOL h264Configured;
61 UINT32 h264Framerate;
62 UINT32 h264Bitrate;
63 UINT32 h264UsageType;
64};
65
69static enum AVPixelFormat video_format_to_av(FREERDP_VIDEO_FORMAT format)
70{
71 switch (format)
72 {
73 /* Compressed formats - not pixel formats */
74 case FREERDP_VIDEO_FORMAT_MJPEG:
75 case FREERDP_VIDEO_FORMAT_H264:
76 return AV_PIX_FMT_NONE;
77
78 /* Planar YUV formats */
79 case FREERDP_VIDEO_FORMAT_YUV420P:
80 return AV_PIX_FMT_YUV420P;
81 case FREERDP_VIDEO_FORMAT_NV12:
82 return AV_PIX_FMT_NV12;
83
84 /* Packed YUV formats */
85 case FREERDP_VIDEO_FORMAT_YUYV422:
86 return AV_PIX_FMT_YUYV422;
87
88 /* RGB formats */
89 case FREERDP_VIDEO_FORMAT_RGB24:
90 return AV_PIX_FMT_RGB24;
91 case FREERDP_VIDEO_FORMAT_RGB32:
92 return AV_PIX_FMT_RGB32;
93
94 case FREERDP_VIDEO_FORMAT_NONE:
95 default:
96 return AV_PIX_FMT_NONE;
97 }
98}
99
100#if defined(WITH_MJPEG_DECODER)
104static FREERDP_VIDEO_FORMAT av_format_to_video(enum AVPixelFormat format)
105{
106 switch (format)
107 {
108 /* Planar YUV formats */
109 case AV_PIX_FMT_YUV420P:
110 return FREERDP_VIDEO_FORMAT_YUV420P;
111
112 case AV_PIX_FMT_NV12:
113 return FREERDP_VIDEO_FORMAT_NV12;
114
115 /* Packed YUV formats */
116 case AV_PIX_FMT_YUYV422:
117 return FREERDP_VIDEO_FORMAT_YUYV422;
118
119 /* RGB formats */
120 case AV_PIX_FMT_RGB24:
121 return FREERDP_VIDEO_FORMAT_RGB24;
122
123 case AV_PIX_FMT_RGB32:
124 return FREERDP_VIDEO_FORMAT_RGB32;
125
126 default:
127 return FREERDP_VIDEO_FORMAT_NONE;
128 }
129}
130#endif
131
132BOOL freerdp_video_available(void)
133{
134 return freerdp_swscale_available() && freerdp_avutil_available();
135}
136
137FREERDP_VIDEO_CONTEXT* freerdp_video_context_new(UINT32 width, UINT32 height)
138{
139 FREERDP_VIDEO_CONTEXT* context = nullptr;
140
141 if (!freerdp_video_available())
142 {
143 WLog_ERR(TAG, "Video codecs not available - FFmpeg not loaded");
144 return nullptr;
145 }
146
147 context = (FREERDP_VIDEO_CONTEXT*)calloc(1, sizeof(FREERDP_VIDEO_CONTEXT));
148 if (!context)
149 return nullptr;
150
151 context->width = width;
152 context->height = height;
153
154#if defined(WITH_MJPEG_DECODER)
155 /* Initialize MJPEG decoder */
156 const AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_MJPEG);
157 if (!codec)
158 {
159 WLog_ERR(TAG, "avcodec_find_decoder failed to find MJPEG codec");
160 goto fail;
161 }
162
163 context->mjpegDecoder = avcodec_alloc_context3(codec);
164 if (!context->mjpegDecoder)
165 {
166 WLog_ERR(TAG, "avcodec_alloc_context3 failed");
167 goto fail;
168 }
169
170 context->mjpegDecoder->width = (int)width;
171 context->mjpegDecoder->height = (int)height;
172 /* Abort on minor errors to skip corrupted frames */
173 context->mjpegDecoder->err_recognition |= AV_EF_EXPLODE;
174
175 if (avcodec_open2(context->mjpegDecoder, codec, nullptr) < 0)
176 {
177 WLog_ERR(TAG, "avcodec_open2 failed");
178 goto fail;
179 }
180
181 context->mjpegPacket = av_packet_alloc();
182 if (!context->mjpegPacket)
183 {
184 WLog_ERR(TAG, "av_packet_alloc failed");
185 goto fail;
186 }
187
188 context->mjpegFrame = av_frame_alloc();
189 if (!context->mjpegFrame)
190 {
191 WLog_ERR(TAG, "av_frame_alloc failed");
192 goto fail;
193 }
194#endif
195
196 return context;
197
198#if defined(WITH_MJPEG_DECODER)
199fail:
200 freerdp_video_context_free(context);
201 return nullptr;
202#endif
203}
204
205void freerdp_video_context_free(FREERDP_VIDEO_CONTEXT* context)
206{
207 if (!context)
208 return;
209
210 if (context->sws)
211 {
212 freerdp_sws_freeContext(context->sws);
213 context->sws = nullptr;
214 }
215
216#if defined(WITH_MJPEG_DECODER)
217 if (context->mjpegFrame)
218 av_frame_free(&context->mjpegFrame);
219
220 if (context->mjpegPacket)
221 {
222 context->mjpegPacket->data = nullptr;
223 context->mjpegPacket->size = 0;
224 av_packet_free(&context->mjpegPacket);
225 }
226
227 if (context->mjpegDecoder)
228 avcodec_free_context(&context->mjpegDecoder);
229#endif
230
231 if (context->h264)
232 {
233 h264_context_free(context->h264);
234 context->h264 = nullptr;
235 }
236
237 free(context);
238}
239
240static UINT32 video_get_h264_bitrate(UINT32 height)
241{
242 static struct
243 {
244 UINT32 height;
245 UINT32 bitrate;
246 } bitrates[] = {
247 { 1080, 2700 }, { 720, 1250 }, { 480, 700 }, { 360, 400 },
248 { 240, 170 }, { 180, 140 }, { 0, 100 },
249 };
250 const size_t nBitrates = ARRAYSIZE(bitrates);
251
252 for (size_t i = 0; i < nBitrates; i++)
253 {
254 if (height >= bitrates[i].height)
255 {
256 UINT32 bitrate = bitrates[i].bitrate;
257 WLog_DBG(TAG, "Auto-calculated H.264 bitrate: %u kbps", bitrate);
258 return bitrate * 1000;
259 }
260 }
261
262 WINPR_ASSERT(FALSE);
263 return 0;
264}
265
266BOOL freerdp_video_context_reconfigure(FREERDP_VIDEO_CONTEXT* context, UINT32 width, UINT32 height,
267 UINT32 framerate, UINT32 bitrate, UINT32 usageType)
268{
269 WINPR_ASSERT(context);
270
271 if (width == 0 || height == 0)
272 {
273 WLog_ERR(TAG, "Invalid dimensions: %ux%u", width, height);
274 return FALSE;
275 }
276
277 if (bitrate == 0)
278 bitrate = video_get_h264_bitrate(height);
279
280 if (!context->h264)
281 {
282 context->h264 = h264_context_new(TRUE);
283 if (!context->h264)
284 {
285 WLog_ERR(TAG, "h264_context_new failed");
286 return FALSE;
287 }
288 }
289
290 if (!h264_context_set_option(context->h264, H264_CONTEXT_OPTION_USAGETYPE, usageType))
291 goto fail;
292
293 if (!h264_context_set_option(context->h264, H264_CONTEXT_OPTION_FRAMERATE, framerate))
294 goto fail;
295
296 if (!h264_context_set_option(context->h264, H264_CONTEXT_OPTION_BITRATE, bitrate))
297 goto fail;
298
299 if (!h264_context_set_option(context->h264, H264_CONTEXT_OPTION_RATECONTROL,
300 H264_RATECONTROL_CQP))
301 goto fail;
302
303 if (!h264_context_set_option(context->h264, H264_CONTEXT_OPTION_QP, 26))
304 goto fail;
305
306 if (!h264_context_set_option(context->h264, H264_CONTEXT_OPTION_HW_ACCEL, FALSE))
307 goto fail;
308
309 if (!context->h264Configured || (context->width != width) || (context->height != height))
310 {
311 if (!h264_context_reset(context->h264, width, height))
312 {
313 WLog_ERR(TAG, "h264_context_reset failed");
314 goto fail;
315 }
316 }
317
318 context->h264Framerate = framerate;
319 context->h264Bitrate = bitrate;
320 context->h264UsageType = usageType;
321 context->h264Configured = TRUE;
322 context->width = width;
323 context->height = height;
324
325 return TRUE;
326
327fail:
328 if (context->h264)
329 {
330 h264_context_free(context->h264);
331 context->h264 = nullptr;
332 }
333 return FALSE;
334}
335
336static BOOL freerdp_video_decode_mjpeg(FREERDP_VIDEO_CONTEXT* context, const BYTE* srcData,
337 size_t srcSize, BYTE* dstData[4], int dstLineSize[4],
338 FREERDP_VIDEO_FORMAT* dstFormat);
339
340static BOOL freerdp_video_convert_to_yuv(FREERDP_VIDEO_CONTEXT* context, const BYTE* srcData[4],
341 const int srcLineSize[4], FREERDP_VIDEO_FORMAT srcFormat,
342 BYTE* dstData[3], const int dstLineSize[3],
343 FREERDP_VIDEO_FORMAT dstFormat, UINT32 width,
344 UINT32 height);
345
346BOOL freerdp_video_sample_convert(FREERDP_VIDEO_CONTEXT* context, FREERDP_VIDEO_FORMAT srcFormat,
347 const void* srcSampleData, size_t srcSampleLength,
348 FREERDP_VIDEO_FORMAT dstFormat, wStream* output)
349{
350 WINPR_ASSERT(context);
351 WINPR_ASSERT(srcSampleData);
352 WINPR_ASSERT(output);
353
354 if (srcFormat == dstFormat)
355 {
356 if (!Stream_EnsureRemainingCapacity(output, srcSampleLength))
357 return FALSE;
358 Stream_Write(output, srcSampleData, srcSampleLength);
359 return TRUE;
360 }
361
362 if (!freerdp_video_available())
363 {
364 WLog_ERR(TAG, "Video codecs not available");
365 return FALSE;
366 }
367
368 if (srcSampleLength == 0)
369 {
370 WLog_ERR(TAG, "Invalid source sample length: 0");
371 return FALSE;
372 }
373
374 if (!freerdp_video_conversion_supported(srcFormat, dstFormat))
375 {
376 WLog_ERR(TAG, "Conversion from format %u to %u not supported", srcFormat, dstFormat);
377 return FALSE;
378 }
379
380 const BOOL srcCompressed =
381 (srcFormat == FREERDP_VIDEO_FORMAT_MJPEG || srcFormat == FREERDP_VIDEO_FORMAT_H264);
382 const BOOL dstCompressed =
383 (dstFormat == FREERDP_VIDEO_FORMAT_MJPEG || dstFormat == FREERDP_VIDEO_FORMAT_H264);
384
385 BYTE* intermediate_data[4] = WINPR_C_ARRAY_INIT;
386 int intermediate_linesize[4] = WINPR_C_ARRAY_INIT;
387 FREERDP_VIDEO_FORMAT intermediate_format = FREERDP_VIDEO_FORMAT_NONE;
388
389 if (srcCompressed)
390 {
391 if (srcFormat == FREERDP_VIDEO_FORMAT_MJPEG)
392 {
393 if (!freerdp_video_decode_mjpeg(context, (const BYTE*)srcSampleData, srcSampleLength,
394 intermediate_data, intermediate_linesize,
395 &intermediate_format))
396 {
397 WLog_ERR(TAG, "MJPEG decoding failed");
398 return FALSE;
399 }
400 }
401 else if (srcFormat == FREERDP_VIDEO_FORMAT_H264)
402 {
403 WLog_ERR(TAG, "H264 decoding not supported");
404 return FALSE;
405 }
406 }
407 else
408 {
409 intermediate_format = srcFormat;
410 }
411
412 if (dstFormat == FREERDP_VIDEO_FORMAT_H264)
413 {
414 if (!context->h264Configured)
415 {
416 WLog_ERR(TAG, "H264 encoder not configured");
417 return FALSE;
418 }
419
420 const UINT32 hwAccel = h264_context_get_option(context->h264, H264_CONTEXT_OPTION_HW_ACCEL);
421 const FREERDP_VIDEO_FORMAT yuvFormat =
422 hwAccel ? FREERDP_VIDEO_FORMAT_NV12 : FREERDP_VIDEO_FORMAT_YUV420P;
423
424 BYTE* yuvData[3] = WINPR_C_ARRAY_INIT;
425 UINT32 yuvStrides[3] = WINPR_C_ARRAY_INIT;
426
427 if (h264_get_yuv_buffer(context->h264, 0, context->width, context->height, yuvData,
428 yuvStrides) < 0)
429 {
430 WLog_ERR(TAG, "h264_get_yuv_buffer failed");
431 return FALSE;
432 }
433
434 int yuvLineSizes[4] = { (int)yuvStrides[0], (int)yuvStrides[1], (int)yuvStrides[2], 0 };
435
436 if (srcCompressed)
437 {
438 const BYTE* cIntermediateData[4] = { intermediate_data[0], intermediate_data[1],
439 intermediate_data[2], intermediate_data[3] };
440 if (!freerdp_video_convert_to_yuv(context, cIntermediateData, intermediate_linesize,
441 intermediate_format, yuvData, yuvLineSizes, yuvFormat,
442 context->width, context->height))
443 {
444 WLog_ERR(TAG, "YUV conversion failed");
445 return FALSE;
446 }
447 }
448 else
449 {
450 BYTE* srcPlanes[4] = WINPR_C_ARRAY_INIT;
451 int srcStrides[4] = WINPR_C_ARRAY_INIT;
452
453 if (!freerdp_video_fill_plane_info(srcPlanes, srcStrides, srcFormat, context->width,
454 context->height, (const BYTE*)srcSampleData))
455 {
456 WLog_ERR(TAG, "Failed to fill plane info");
457 return FALSE;
458 }
459
460 const BYTE* cSrcPlanes[4] = { srcPlanes[0], srcPlanes[1], srcPlanes[2], srcPlanes[3] };
461 if (!freerdp_video_convert_to_yuv(context, cSrcPlanes, srcStrides, srcFormat, yuvData,
462 yuvLineSizes, yuvFormat, context->width,
463 context->height))
464 {
465 WLog_ERR(TAG, "YUV conversion failed");
466 return FALSE;
467 }
468 }
469
470 BYTE* h264Data = nullptr;
471 UINT32 h264Size = 0;
472
473 if (h264_compress(context->h264, &h264Data, &h264Size) < 0)
474 {
475 WLog_ERR(TAG, "H264 compression failed");
476 return FALSE;
477 }
478
479 if (!Stream_EnsureRemainingCapacity(output, h264Size))
480 {
481 WLog_ERR(TAG, "Failed to ensure stream capacity");
482 return FALSE;
483 }
484
485 Stream_Write(output, h264Data, h264Size);
486 return TRUE;
487 }
488 else if (dstCompressed)
489 {
490 WLog_ERR(TAG, "Only H264 encoding is supported");
491 return FALSE;
492 }
493
494 WLog_ERR(TAG, "Raw format output not yet implemented");
495 return FALSE;
496}
497
498static BOOL freerdp_video_decode_mjpeg(FREERDP_VIDEO_CONTEXT* context, const BYTE* srcData,
499 size_t srcSize, BYTE* dstData[4], int dstLineSize[4],
500 FREERDP_VIDEO_FORMAT* dstFormat)
501{
502#if defined(WITH_MJPEG_DECODER)
503 WINPR_ASSERT(context);
504 WINPR_ASSERT(srcData);
505 WINPR_ASSERT(dstData);
506 WINPR_ASSERT(dstLineSize);
507 WINPR_ASSERT(dstFormat);
508
509 if (!context->mjpegDecoder)
510 {
511 WLog_ERR(TAG, "MJPEG decoder not initialized");
512 return FALSE;
513 }
514
515 context->mjpegPacket->data = WINPR_CAST_CONST_PTR_AWAY(srcData, uint8_t*);
516 WINPR_ASSERT(srcSize <= INT32_MAX);
517 context->mjpegPacket->size = (int)srcSize;
518
519 if (avcodec_send_packet(context->mjpegDecoder, context->mjpegPacket) < 0)
520 {
521 WLog_ERR(TAG, "avcodec_send_packet failed");
522 return FALSE;
523 }
524
525 if (avcodec_receive_frame(context->mjpegDecoder, context->mjpegFrame) < 0)
526 {
527 WLog_ERR(TAG, "avcodec_receive_frame failed");
528 return FALSE;
529 }
530
531 /* Copy plane pointers and line sizes */
532 for (size_t i = 0; i < 4; i++)
533 {
534 dstData[i] = context->mjpegFrame->data[i];
535 dstLineSize[i] = context->mjpegFrame->linesize[i];
536 }
537
538 /* Convert pixel format */
539 *dstFormat = av_format_to_video(context->mjpegDecoder->pix_fmt);
540
541 return TRUE;
542#else
543 WINPR_UNUSED(context);
544 WINPR_UNUSED(srcData);
545 WINPR_UNUSED(srcSize);
546 WINPR_UNUSED(dstData);
547 WINPR_UNUSED(dstLineSize);
548 WINPR_UNUSED(dstFormat);
549 WLog_ERR(TAG, "MJPEG decoder not available (requires direct FFmpeg linking)");
550 return FALSE;
551#endif
552}
553
554static BOOL freerdp_video_convert_to_yuv(FREERDP_VIDEO_CONTEXT* context, const BYTE* srcData[4],
555 const int srcLineSize[4], FREERDP_VIDEO_FORMAT srcFormat,
556 BYTE* dstData[3], const int dstLineSize[3],
557 FREERDP_VIDEO_FORMAT dstFormat, UINT32 width,
558 UINT32 height)
559{
560 WINPR_ASSERT(srcData);
561 WINPR_ASSERT(srcLineSize);
562 WINPR_ASSERT(dstData);
563 WINPR_ASSERT(dstLineSize);
564
565 if (!freerdp_swscale_available())
566 {
567 WLog_ERR(TAG, "swscale not available - install FFmpeg to enable video processing");
568 return FALSE;
569 }
570
571 enum AVPixelFormat srcPixFmt = video_format_to_av(srcFormat);
572 enum AVPixelFormat dstPixFmt = video_format_to_av(dstFormat);
573
574 if (srcPixFmt == AV_PIX_FMT_NONE || dstPixFmt == AV_PIX_FMT_NONE)
575 {
576 WLog_ERR(TAG, "Unsupported pixel format");
577 return FALSE;
578 }
579
580 /* Create or reuse swscale context */
581 struct SwsContext* sws = nullptr;
582 BOOL needFree = FALSE;
583
584 if (context && context->sws)
585 {
586 sws = context->sws;
587 }
588 else
589 {
590 sws = freerdp_sws_getContext((int)width, (int)height, srcPixFmt, (int)width, (int)height,
591 dstPixFmt, 0, nullptr, nullptr, nullptr);
592 if (!sws)
593 {
594 WLog_ERR(TAG, "sws_getContext failed");
595 return FALSE;
596 }
597
598 if (context)
599 context->sws = sws;
600 else
601 needFree = TRUE;
602 }
603
604 /* Perform conversion */
605 const BYTE* cSrcData[4] = { srcData[0], srcData[1], srcData[2], srcData[3] };
606
607 /* sws_scale expects 4-element arrays, but caller may provide 3-element arrays for YUV */
608 uint8_t* localDstData[4] = { dstData[0], dstData[1], dstData[2], nullptr };
609 int localDstLineSize[4] = { dstLineSize[0], dstLineSize[1], dstLineSize[2], 0 };
610
611 const int result = freerdp_sws_scale(sws, cSrcData, srcLineSize, 0, (int)height, localDstData,
612 localDstLineSize);
613
614 if (needFree)
615 {
616 freerdp_sws_freeContext(sws);
617 }
618
619 return (result > 0);
620}
621
622static BOOL freerdp_video_fill_plane_info(BYTE* data[4], int lineSize[4],
623 FREERDP_VIDEO_FORMAT format, UINT32 width, UINT32 height,
624 const BYTE* buffer)
625{
626 WINPR_ASSERT(data);
627 WINPR_ASSERT(lineSize);
628
629 enum AVPixelFormat pixFmt = video_format_to_av(format);
630 if (pixFmt == AV_PIX_FMT_NONE)
631 {
632 WLog_ERR(TAG, "Unsupported pixel format");
633 return FALSE;
634 }
635
636 if (!freerdp_avutil_available())
637 {
638 WLog_ERR(TAG, "avutil not available");
639 return FALSE;
640 }
641
642 if (freerdp_av_image_fill_linesizes(lineSize, pixFmt, (int)width) < 0)
643 {
644 WLog_ERR(TAG, "av_image_fill_linesizes failed");
645 return FALSE;
646 }
647
648 if (freerdp_av_image_fill_pointers(data, pixFmt, (int)height,
649 WINPR_CAST_CONST_PTR_AWAY(buffer, BYTE*), lineSize) < 0)
650 {
651 WLog_ERR(TAG, "av_image_fill_pointers failed");
652 return FALSE;
653 }
654
655 return TRUE;
656}
657
658BOOL freerdp_video_conversion_supported(FREERDP_VIDEO_FORMAT srcFormat,
659 FREERDP_VIDEO_FORMAT dstFormat)
660{
661 if (srcFormat == dstFormat)
662 return TRUE;
663
664 if (!freerdp_video_available())
665 return FALSE;
666
667 if (srcFormat == FREERDP_VIDEO_FORMAT_MJPEG)
668 {
669#if !defined(WITH_MJPEG_DECODER)
670 return FALSE;
671#endif
672 }
673 else if (srcFormat == FREERDP_VIDEO_FORMAT_H264)
674 {
675 return FALSE;
676 }
677
678 if (dstFormat == FREERDP_VIDEO_FORMAT_MJPEG)
679 {
680 return FALSE;
681 }
682 else if (dstFormat == FREERDP_VIDEO_FORMAT_H264)
683 {
684#if !defined(WITH_OPENH264) && !defined(WITH_VIDEO_FFMPEG) && !defined(WITH_MEDIA_FOUNDATION) && \
685 !defined(WITH_MEDIACODEC)
686 return FALSE;
687#endif
688 }
689
690 return TRUE;
691}
692
693#else /* !WITH_SWSCALE */
694
695/* Stubs when swscale is not available */
696
697BOOL freerdp_video_available(void)
698{
699 return FALSE;
700}
701
702FREERDP_VIDEO_CONTEXT* freerdp_video_context_new(WINPR_ATTR_UNUSED UINT32 width,
703 WINPR_ATTR_UNUSED UINT32 height)
704{
705 WINPR_UNUSED(width);
706 WINPR_UNUSED(height);
707 return calloc(1, sizeof(char));
708}
709
710void freerdp_video_context_free(FREERDP_VIDEO_CONTEXT* context)
711{
712 free(context);
713}
714
715BOOL freerdp_video_conversion_supported(FREERDP_VIDEO_FORMAT srcFormat,
716 FREERDP_VIDEO_FORMAT dstFormat)
717{
718 if (srcFormat == dstFormat)
719 return TRUE;
720 return FALSE;
721}
722
723BOOL freerdp_video_context_reconfigure(WINPR_ATTR_UNUSED FREERDP_VIDEO_CONTEXT* context,
724 WINPR_ATTR_UNUSED UINT32 width,
725 WINPR_ATTR_UNUSED UINT32 height,
726 WINPR_ATTR_UNUSED UINT32 framerate,
727 WINPR_ATTR_UNUSED UINT32 bitrate,
728 WINPR_ATTR_UNUSED UINT32 usageType)
729{
730 return TRUE;
731}
732
733BOOL freerdp_video_sample_convert(WINPR_ATTR_UNUSED FREERDP_VIDEO_CONTEXT* context,
734 FREERDP_VIDEO_FORMAT srcFormat, const void* srcSampleData,
735 size_t srcSampleLength, FREERDP_VIDEO_FORMAT dstFormat,
736 wStream* output)
737{
738 if (srcFormat == dstFormat)
739 {
740 if (!Stream_EnsureRemainingCapacity(output, srcSampleLength))
741 return FALSE;
742
743 Stream_Write(output, srcSampleData, srcSampleLength);
744 return TRUE;
745 }
746
747 return FALSE;
748}
749
750#endif /* WITH_SWSCALE */