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 %d", ecamFormat);
 
  219      return AV_PIX_FMT_NONE;
 
  229static BOOL ecam_init_sws_context(
CameraDeviceStream* stream, 
enum AVPixelFormat pixFormat)
 
  231  WINPR_ASSERT(stream);
 
  239    case AV_PIX_FMT_YUVJ411P:
 
  240      pixFormat = AV_PIX_FMT_YUV411P;
 
  243    case AV_PIX_FMT_YUVJ420P:
 
  244      pixFormat = AV_PIX_FMT_YUV420P;
 
  247    case AV_PIX_FMT_YUVJ422P:
 
  248      pixFormat = AV_PIX_FMT_YUV422P;
 
  251    case AV_PIX_FMT_YUVJ440P:
 
  252      pixFormat = AV_PIX_FMT_YUV440P;
 
  255    case AV_PIX_FMT_YUVJ444P:
 
  256      pixFormat = AV_PIX_FMT_YUV444P;
 
  263  const int width = (int)stream->currMediaType.Width;
 
  264  const int height = (int)stream->currMediaType.Height;
 
  266  const enum AVPixelFormat outPixFormat =
 
  267      h264_context_get_option(stream->h264, H264_CONTEXT_OPTION_HW_ACCEL) ? AV_PIX_FMT_NV12
 
  268                                                                          : AV_PIX_FMT_YUV420P;
 
  271      sws_getContext(width, height, pixFormat, width, height, outPixFormat, 0, NULL, NULL, NULL);
 
  274    WLog_ERR(TAG, 
"sws_getContext failed");
 
  286static BOOL ecam_encoder_compress_h264(
CameraDeviceStream* stream, 
const BYTE* srcData,
 
  287                                       size_t srcSize, BYTE** ppDstData, 
size_t* pDstSize)
 
  290  BYTE* srcSlice[4] = { 0 };
 
  291  int srcLineSizes[4] = { 0 };
 
  292  BYTE* yuvData[3] = { 0 };
 
  293  UINT32 yuvLineSizes[3] = { 0 };
 
  294  prim_size_t size = { stream->currMediaType.Width, stream->currMediaType.Height };
 
  295  CAM_MEDIA_FORMAT inputFormat = streamInputFormat(stream);
 
  296  enum AVPixelFormat pixFormat = AV_PIX_FMT_NONE;
 
  298#if defined(WITH_INPUT_FORMAT_H264) 
  299  if (inputFormat == CAM_MEDIA_FORMAT_MJPG_H264)
 
  302        demux_uvcH264(srcData, srcSize, stream->h264Frame, stream->h264FrameMaxSize);
 
  303    dstSize = WINPR_ASSERTING_INT_CAST(uint32_t, rc);
 
  304    *ppDstData = stream->h264Frame;
 
  311#if defined(WITH_INPUT_FORMAT_MJPG) 
  312      if (inputFormat == CAM_MEDIA_FORMAT_MJPG)
 
  314    stream->avInputPkt->data = WINPR_CAST_CONST_PTR_AWAY(srcData, uint8_t*);
 
  315    WINPR_ASSERT(srcSize <= INT32_MAX);
 
  316    stream->avInputPkt->size = (int)srcSize;
 
  318    if (avcodec_send_packet(stream->avContext, stream->avInputPkt) < 0)
 
  320      WLog_ERR(TAG, 
"avcodec_send_packet failed");
 
  324    if (avcodec_receive_frame(stream->avContext, stream->avOutFrame) < 0)
 
  326      WLog_ERR(TAG, 
"avcodec_receive_frame failed");
 
  330    for (
size_t i = 0; i < 4; i++)
 
  332      srcSlice[i] = stream->avOutFrame->data[i];
 
  333      srcLineSizes[i] = stream->avOutFrame->linesize[i];
 
  337    pixFormat = stream->avContext->pix_fmt;
 
  342    pixFormat = ecamToAVPixFormat(inputFormat);
 
  344    if (av_image_fill_linesizes(srcLineSizes, pixFormat, (
int)size.width) < 0)
 
  346      WLog_ERR(TAG, 
"av_image_fill_linesizes failed");
 
  350    if (av_image_fill_pointers(srcSlice, pixFormat, (
int)size.height,
 
  351                               WINPR_CAST_CONST_PTR_AWAY(srcData, BYTE*), srcLineSizes) < 0)
 
  353      WLog_ERR(TAG, 
"av_image_fill_pointers failed");
 
  359  if (h264_get_yuv_buffer(stream->h264, 0, size.width, size.height, yuvData, yuvLineSizes) < 0)
 
  363  if (!ecam_init_sws_context(stream, pixFormat))
 
  366  const BYTE* cSrcSlice[4] = { srcSlice[0], srcSlice[1], srcSlice[2], srcSlice[3] };
 
  367  if (sws_scale(stream->sws, cSrcSlice, srcLineSizes, 0, (
int)size.height, yuvData,
 
  368                (
int*)yuvLineSizes) <= 0)
 
  372  if (h264_compress(stream->h264, ppDstData, &dstSize) < 0)
 
  386  WINPR_ASSERT(stream);
 
  390    sws_freeContext(stream->sws);
 
  394#if defined(WITH_INPUT_FORMAT_MJPG) 
  395  if (stream->avOutFrame)
 
  396    av_frame_free(&stream->avOutFrame); 
 
  398  if (stream->avInputPkt)
 
  400    stream->avInputPkt->data = NULL;
 
  401    stream->avInputPkt->size = 0;
 
  402    av_packet_free(&stream->avInputPkt); 
 
  405  if (stream->avContext)
 
  406    avcodec_free_context(&stream->avContext); 
 
  409#if defined(WITH_INPUT_FORMAT_H264) 
  410  if (stream->h264Frame)
 
  412    free(stream->h264Frame);
 
  413    stream->h264Frame = NULL;
 
  419    h264_context_free(stream->h264);
 
  424#if defined(WITH_INPUT_FORMAT_MJPG) 
  432  WINPR_ASSERT(stream);
 
  434  const AVCodec* avcodec = avcodec_find_decoder(AV_CODEC_ID_MJPEG);
 
  437    WLog_ERR(TAG, 
"avcodec_find_decoder failed to find MJPEG codec");
 
  441  stream->avContext = avcodec_alloc_context3(avcodec);
 
  442  if (!stream->avContext)
 
  444    WLog_ERR(TAG, 
"avcodec_alloc_context3 failed");
 
  448  stream->avContext->width = WINPR_ASSERTING_INT_CAST(
int, stream->currMediaType.Width);
 
  449  stream->avContext->height = WINPR_ASSERTING_INT_CAST(
int, stream->currMediaType.Height);
 
  453  stream->avContext->err_recognition |= AV_EF_EXPLODE;
 
  455  if (avcodec_open2(stream->avContext, avcodec, NULL) < 0)
 
  457    WLog_ERR(TAG, 
"avcodec_open2 failed");
 
  461  stream->avInputPkt = av_packet_alloc();
 
  462  if (!stream->avInputPkt)
 
  464    WLog_ERR(TAG, 
"av_packet_alloc failed");
 
  468  stream->avOutFrame = av_frame_alloc();
 
  469  if (!stream->avOutFrame)
 
  471    WLog_ERR(TAG, 
"av_frame_alloc failed");
 
  486  WINPR_ASSERT(stream);
 
  488#if defined(WITH_INPUT_FORMAT_H264) 
  489  if (streamInputFormat(stream) == CAM_MEDIA_FORMAT_MJPG_H264)
 
  491    stream->h264FrameMaxSize = 1ULL * stream->currMediaType.Width *
 
  492                               stream->currMediaType.Height; 
 
  493    stream->h264Frame = (BYTE*)calloc(stream->h264FrameMaxSize, 
sizeof(BYTE));
 
  499    stream->h264 = h264_context_new(TRUE);
 
  503    WLog_ERR(TAG, 
"h264_context_new failed");
 
  507  if (!h264_context_set_option(stream->h264, H264_CONTEXT_OPTION_USAGETYPE,
 
  508                               H264_CAMERA_VIDEO_REAL_TIME))
 
  511  if (!h264_context_set_option(stream->h264, H264_CONTEXT_OPTION_FRAMERATE,
 
  512                               stream->currMediaType.FrameRateNumerator /
 
  513                                   stream->currMediaType.FrameRateDenominator))
 
  516  if (!h264_context_set_option(stream->h264, H264_CONTEXT_OPTION_BITRATE,
 
  517                               h264_get_max_bitrate(stream->currMediaType.Height)))
 
  523  if (!h264_context_set_option(stream->h264, H264_CONTEXT_OPTION_RATECONTROL,
 
  524                               H264_RATECONTROL_CQP))
 
  530  if (!h264_context_set_option(stream->h264, H264_CONTEXT_OPTION_QP, 26))
 
  534  if (!h264_context_set_option(stream->h264, H264_CONTEXT_OPTION_HW_ACCEL, TRUE))
 
  537  if (!h264_context_reset(stream->h264, stream->currMediaType.Width,
 
  538                          stream->currMediaType.Height))
 
  540    WLog_ERR(TAG, 
"h264_context_reset failed");
 
  544#if defined(WITH_INPUT_FORMAT_MJPG) 
  545  if (streamInputFormat(stream) == CAM_MEDIA_FORMAT_MJPG && !ecam_init_mjpeg_decoder(stream))
 
  552  ecam_encoder_context_free_h264(stream);
 
  563  CAM_MEDIA_FORMAT format = streamOutputFormat(stream);
 
  567    case CAM_MEDIA_FORMAT_H264:
 
  568      return ecam_encoder_context_init_h264(stream);
 
  571      WLog_ERR(TAG, 
"Unsupported output format %d", format);
 
  583  CAM_MEDIA_FORMAT format = streamOutputFormat(stream);
 
  586    case CAM_MEDIA_FORMAT_H264:
 
  587      ecam_encoder_context_free_h264(stream);
 
  601BOOL ecam_encoder_compress(
CameraDeviceStream* stream, 
const BYTE* srcData, 
size_t srcSize,
 
  602                           BYTE** ppDstData, 
size_t* pDstSize)
 
  604  CAM_MEDIA_FORMAT format = streamOutputFormat(stream);
 
  607    case CAM_MEDIA_FORMAT_H264:
 
  608      return ecam_encoder_compress_h264(stream, srcData, srcSize, ppDstData, pDstSize);
 
  610      WLog_ERR(TAG, 
"Unsupported output format %d", format);