20#include <winpr/assert.h>
21#include <winpr/winpr.h>
25#define TAG CHANNELS_TAG("rdpecam-video.client")
27#if defined(WITH_INPUT_FORMAT_H264)
45static size_t demux_uvcH264(
const BYTE* srcData,
size_t srcSize, BYTE* h264_data,
48 WINPR_ASSERT(h264_data);
49 WINPR_ASSERT(srcData);
53 WLog_ERR(TAG,
"Expected srcSize >= 30, got %" PRIuz, srcSize);
56 const uint8_t* spl = NULL;
57 uint8_t* ph264 = h264_data;
62 for (
const uint8_t* sp = srcData; sp < srcData + srcSize - 30; sp++)
64 if (sp[0] == 0xFF && sp[1] == 0xE4)
73 WLog_ERR(TAG,
"Expected 1st APP4 marker but none found");
77 if (spl > srcData + srcSize - 4)
79 WLog_ERR(TAG,
"Payload + Header size bigger than srcData buffer");
86 uint16_t length = (uint16_t)(spl[0] << 8) & UINT16_MAX;
87 length |= (uint16_t)spl[1];
91 uint16_t header_length = (uint16_t)spl[2];
92 header_length |= (uint16_t)spl[3] << 8;
95 if (spl > srcData + srcSize)
97 WLog_ERR(TAG,
"Header size bigger than srcData buffer");
102 uint32_t payload_size = (uint32_t)spl[0] << 0;
103 payload_size |= (uint32_t)spl[1] << 8;
104 payload_size |= (uint32_t)spl[2] << 16;
105 payload_size |= (uint32_t)spl[3] << 24;
107 if (payload_size > h264_max_size)
109 WLog_ERR(TAG,
"Payload size bigger than h264_data buffer");
114 const uint8_t* epl = spl + payload_size;
116 if (epl > srcData + srcSize)
118 WLog_ERR(TAG,
"Payload size bigger than srcData buffer");
122 length -= header_length + 6;
125 memcpy(ph264, spl, length);
130 while (epl > spl + 4)
132 if (spl[0] != 0xFF || spl[1] != 0xE4)
134 WLog_ERR(TAG,
"Expected 2nd+ APP4 marker but none found");
135 const intptr_t diff = ph264 - h264_data;
136 return WINPR_ASSERTING_INT_CAST(
size_t, diff);
140 length = (uint16_t)(spl[2] << 8) & UINT16_MAX;
141 length |= (uint16_t)spl[3];
144 WLog_ERR(TAG,
"Expected 2nd+ APP4 length >= 2 but have %" PRIu16, length);
152 memcpy(ph264, spl, length);
157 const intptr_t diff = ph264 - h264_data;
158 return WINPR_ASSERTING_INT_CAST(
size_t, diff);
167UINT32 h264_get_max_bitrate(UINT32 height)
169 static struct Bitrates
179 { 1080, 2700 }, { 720, 1250 }, { 480, 700 }, { 360, 400 },
180 { 240, 170 }, { 180, 140 }, { 0, 100 },
182 const size_t nBitrates = ARRAYSIZE(bitrates);
184 for (
size_t i = 0; i < nBitrates; i++)
186 if (height >= bitrates[i].height)
188 UINT32 bitrate = bitrates[i].bitrate;
189 WLog_DBG(TAG,
"Setting h264 max bitrate: %u kbps", bitrate);
190 return bitrate * 1000;
203static enum AVPixelFormat ecamToAVPixFormat(CAM_MEDIA_FORMAT ecamFormat)
207 case CAM_MEDIA_FORMAT_YUY2:
208 return AV_PIX_FMT_YUYV422;
209 case CAM_MEDIA_FORMAT_NV12:
210 return AV_PIX_FMT_NV12;
211 case CAM_MEDIA_FORMAT_I420:
212 return AV_PIX_FMT_YUV420P;
213 case CAM_MEDIA_FORMAT_RGB24:
214 return AV_PIX_FMT_RGB24;
215 case CAM_MEDIA_FORMAT_RGB32:
216 return AV_PIX_FMT_RGB32;
218 WLog_ERR(TAG,
"Unsupported ecamFormat %u", ecamFormat);
219 return AV_PIX_FMT_NONE;
227 sws_freeContext(stream->sws);
236 if (stream->swsWidth != stream->currMediaType.Width)
238 if (stream->swsHeight != stream->currMediaType.Height)
240 if (stream->currMediaType.Width > INT32_MAX)
242 if (stream->currMediaType.Height > INT32_MAX)
253static BOOL ecam_init_sws_context(
CameraDeviceStream* stream,
enum AVPixelFormat pixFormat)
255 WINPR_ASSERT(stream);
257 if (stream->currMediaType.Width > INT32_MAX)
259 if (stream->currMediaType.Height > INT32_MAX)
262 if (ecam_sws_valid(stream))
265 ecam_sws_free(stream);
270 case AV_PIX_FMT_YUVJ411P:
271 pixFormat = AV_PIX_FMT_YUV411P;
274 case AV_PIX_FMT_YUVJ420P:
275 pixFormat = AV_PIX_FMT_YUV420P;
278 case AV_PIX_FMT_YUVJ422P:
279 pixFormat = AV_PIX_FMT_YUV422P;
282 case AV_PIX_FMT_YUVJ440P:
283 pixFormat = AV_PIX_FMT_YUV440P;
286 case AV_PIX_FMT_YUVJ444P:
287 pixFormat = AV_PIX_FMT_YUV444P;
294 stream->swsWidth = stream->currMediaType.Width;
295 stream->swsHeight = stream->currMediaType.Height;
296 const int width = WINPR_ASSERTING_INT_CAST(
int, stream->currMediaType.Width);
297 const int height = WINPR_ASSERTING_INT_CAST(
int, stream->currMediaType.Height);
299 const enum AVPixelFormat outPixFormat =
300 h264_context_get_option(stream->h264, H264_CONTEXT_OPTION_HW_ACCEL) ? AV_PIX_FMT_NV12
301 : AV_PIX_FMT_YUV420P;
304 sws_getContext(width, height, pixFormat, width, height, outPixFormat, 0, NULL, NULL, NULL);
307 WLog_ERR(TAG,
"sws_getContext failed");
319static BOOL ecam_encoder_compress_h264(
CameraDeviceStream* stream,
const BYTE* srcData,
320 size_t srcSize, BYTE** ppDstData,
size_t* pDstSize)
323 BYTE* srcSlice[4] = { 0 };
324 int srcLineSizes[4] = { 0 };
325 BYTE* yuvData[3] = { 0 };
326 UINT32 yuvLineSizes[3] = { 0 };
327 prim_size_t size = { stream->currMediaType.Width, stream->currMediaType.Height };
328 CAM_MEDIA_FORMAT inputFormat = streamInputFormat(stream);
329 enum AVPixelFormat pixFormat = AV_PIX_FMT_NONE;
331#if defined(WITH_INPUT_FORMAT_H264)
332 if (inputFormat == CAM_MEDIA_FORMAT_MJPG_H264)
335 demux_uvcH264(srcData, srcSize, stream->h264Frame, stream->h264FrameMaxSize);
336 dstSize = WINPR_ASSERTING_INT_CAST(uint32_t, rc);
337 *ppDstData = stream->h264Frame;
344#if defined(WITH_INPUT_FORMAT_MJPG)
345 if (inputFormat == CAM_MEDIA_FORMAT_MJPG)
347 stream->avInputPkt->data = WINPR_CAST_CONST_PTR_AWAY(srcData, uint8_t*);
348 WINPR_ASSERT(srcSize <= INT32_MAX);
349 stream->avInputPkt->size = (int)srcSize;
351 if (avcodec_send_packet(stream->avContext, stream->avInputPkt) < 0)
353 WLog_ERR(TAG,
"avcodec_send_packet failed");
357 if (avcodec_receive_frame(stream->avContext, stream->avOutFrame) < 0)
359 WLog_ERR(TAG,
"avcodec_receive_frame failed");
363 for (
size_t i = 0; i < 4; i++)
365 srcSlice[i] = stream->avOutFrame->data[i];
366 srcLineSizes[i] = stream->avOutFrame->linesize[i];
370 pixFormat = stream->avContext->pix_fmt;
375 pixFormat = ecamToAVPixFormat(inputFormat);
377 if (av_image_fill_linesizes(srcLineSizes, pixFormat, (
int)size.width) < 0)
379 WLog_ERR(TAG,
"av_image_fill_linesizes failed");
383 if (av_image_fill_pointers(srcSlice, pixFormat, (
int)size.height,
384 WINPR_CAST_CONST_PTR_AWAY(srcData, BYTE*), srcLineSizes) < 0)
386 WLog_ERR(TAG,
"av_image_fill_pointers failed");
392 if (h264_get_yuv_buffer(stream->h264, 0, size.width, size.height, yuvData, yuvLineSizes) < 0)
396 if (!ecam_init_sws_context(stream, pixFormat))
399 const BYTE* cSrcSlice[4] = { srcSlice[0], srcSlice[1], srcSlice[2], srcSlice[3] };
400 if (sws_scale(stream->sws, cSrcSlice, srcLineSizes, 0, (
int)size.height, yuvData,
401 (
int*)yuvLineSizes) <= 0)
405 if (h264_compress(stream->h264, ppDstData, &dstSize) < 0)
419 WINPR_ASSERT(stream);
421 ecam_sws_free(stream);
423#if defined(WITH_INPUT_FORMAT_MJPG)
424 if (stream->avOutFrame)
425 av_frame_free(&stream->avOutFrame);
427 if (stream->avInputPkt)
429 stream->avInputPkt->data = NULL;
430 stream->avInputPkt->size = 0;
431 av_packet_free(&stream->avInputPkt);
434 if (stream->avContext)
435 avcodec_free_context(&stream->avContext);
438#if defined(WITH_INPUT_FORMAT_H264)
439 if (stream->h264Frame)
441 free(stream->h264Frame);
442 stream->h264Frame = NULL;
448 h264_context_free(stream->h264);
453#if defined(WITH_INPUT_FORMAT_MJPG)
461 WINPR_ASSERT(stream);
463 const AVCodec* avcodec = avcodec_find_decoder(AV_CODEC_ID_MJPEG);
466 WLog_ERR(TAG,
"avcodec_find_decoder failed to find MJPEG codec");
470 stream->avContext = avcodec_alloc_context3(avcodec);
471 if (!stream->avContext)
473 WLog_ERR(TAG,
"avcodec_alloc_context3 failed");
477 stream->avContext->width = WINPR_ASSERTING_INT_CAST(
int, stream->currMediaType.Width);
478 stream->avContext->height = WINPR_ASSERTING_INT_CAST(
int, stream->currMediaType.Height);
482 stream->avContext->err_recognition |= AV_EF_EXPLODE;
484 if (avcodec_open2(stream->avContext, avcodec, NULL) < 0)
486 WLog_ERR(TAG,
"avcodec_open2 failed");
490 stream->avInputPkt = av_packet_alloc();
491 if (!stream->avInputPkt)
493 WLog_ERR(TAG,
"av_packet_alloc failed");
497 stream->avOutFrame = av_frame_alloc();
498 if (!stream->avOutFrame)
500 WLog_ERR(TAG,
"av_frame_alloc failed");
515 WINPR_ASSERT(stream);
517#if defined(WITH_INPUT_FORMAT_H264)
518 if (streamInputFormat(stream) == CAM_MEDIA_FORMAT_MJPG_H264)
520 stream->h264FrameMaxSize = 1ULL * stream->currMediaType.Width *
521 stream->currMediaType.Height;
522 stream->h264Frame = (BYTE*)calloc(stream->h264FrameMaxSize,
sizeof(BYTE));
528 stream->h264 = h264_context_new(TRUE);
532 WLog_ERR(TAG,
"h264_context_new failed");
536 if (!h264_context_set_option(stream->h264, H264_CONTEXT_OPTION_USAGETYPE,
537 H264_CAMERA_VIDEO_REAL_TIME))
540 if (!h264_context_set_option(stream->h264, H264_CONTEXT_OPTION_FRAMERATE,
541 stream->currMediaType.FrameRateNumerator /
542 stream->currMediaType.FrameRateDenominator))
545 if (!h264_context_set_option(stream->h264, H264_CONTEXT_OPTION_BITRATE,
546 h264_get_max_bitrate(stream->currMediaType.Height)))
552 if (!h264_context_set_option(stream->h264, H264_CONTEXT_OPTION_RATECONTROL,
553 H264_RATECONTROL_CQP))
559 if (!h264_context_set_option(stream->h264, H264_CONTEXT_OPTION_QP, 26))
563 if (!h264_context_set_option(stream->h264, H264_CONTEXT_OPTION_HW_ACCEL, TRUE))
566 if (!h264_context_reset(stream->h264, stream->currMediaType.Width,
567 stream->currMediaType.Height))
569 WLog_ERR(TAG,
"h264_context_reset failed");
573#if defined(WITH_INPUT_FORMAT_MJPG)
574 if (streamInputFormat(stream) == CAM_MEDIA_FORMAT_MJPG && !ecam_init_mjpeg_decoder(stream))
581 ecam_encoder_context_free_h264(stream);
592 CAM_MEDIA_FORMAT format = streamOutputFormat(stream);
596 case CAM_MEDIA_FORMAT_H264:
597 return ecam_encoder_context_init_h264(stream);
600 WLog_ERR(TAG,
"Unsupported output format %u", format);
612 CAM_MEDIA_FORMAT format = streamOutputFormat(stream);
615 case CAM_MEDIA_FORMAT_H264:
616 ecam_encoder_context_free_h264(stream);
630BOOL ecam_encoder_compress(
CameraDeviceStream* stream,
const BYTE* srcData,
size_t srcSize,
631 BYTE** ppDstData,
size_t* pDstSize)
633 CAM_MEDIA_FORMAT format = streamOutputFormat(stream);
636 case CAM_MEDIA_FORMAT_H264:
637 return ecam_encoder_compress_h264(stream, srcData, srcSize, ppDstData, pDstSize);
639 WLog_ERR(TAG,
"Unsupported output format %u", format);