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