static GstVaapiEncoderStatus
set_context_info (GstVaapiEncoder * base_encoder)
{
  GstVaapiEncoderJpeg *encoder = GST_VAAPI_ENCODER_JPEG_CAST (base_encoder);
  GstVideoInfo *const vip = GST_VAAPI_ENCODER_VIDEO_INFO (encoder);

  /* Maximum sizes for common headers (in bytes) */
  enum
  {
    MAX_APP_HDR_SIZE = 20,
    MAX_FRAME_HDR_SIZE = 19,
    MAX_QUANT_TABLE_SIZE = 138,
    MAX_HUFFMAN_TABLE_SIZE = 432,
    MAX_SCAN_HDR_SIZE = 14
  };

  if (!ensure_hw_profile (encoder))
    return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_PROFILE;

  base_encoder->num_ref_frames = 0;

  /* Only YUV 4:2:0 formats are supported for now. */
  base_encoder->codedbuf_size = GST_ROUND_UP_16 (vip->width) *
      GST_ROUND_UP_16 (vip->height) * 3 / 2;

  base_encoder->codedbuf_size += MAX_APP_HDR_SIZE + MAX_FRAME_HDR_SIZE +
      MAX_QUANT_TABLE_SIZE + MAX_HUFFMAN_TABLE_SIZE + MAX_SCAN_HDR_SIZE;

  return GST_VAAPI_ENCODER_STATUS_SUCCESS;
}
static inline gboolean
is_chroma_type_supported (GstVaapiEncoder * encoder)
{
  GstVaapiContextInfo *const cip = &encoder->context_info;
  const GstVideoFormat fmt =
    GST_VIDEO_INFO_FORMAT (GST_VAAPI_ENCODER_VIDEO_INFO (encoder));
  guint format = 0;

  if (fmt == GST_VIDEO_FORMAT_ENCODED)
    return TRUE;

  if (cip->chroma_type != GST_VAAPI_CHROMA_TYPE_YUV420 &&
      cip->chroma_type != GST_VAAPI_CHROMA_TYPE_YUV422)
    goto unsupported;

  if (!get_config_attribute (encoder, VAConfigAttribRTFormat, &format))
    return FALSE;

  if (!(format & from_GstVaapiChromaType (cip->chroma_type)))
    goto unsupported;

  return TRUE;

unsupported:
  {
    GST_ERROR ("We only support YUV 4:2:0 and YUV 4:2:2 for encoding. "
        "Please try to use vaapipostproc to convert the input format.");
    return FALSE;
  }
}
/* based on upstream gst-plugins-good jpegencoder */
static void
generate_sampling_factors (GstVaapiEncoderJpeg * encoder)
{
  GstVideoInfo *vinfo;
  gint i;

  vinfo = GST_VAAPI_ENCODER_VIDEO_INFO (encoder);

  if (GST_VIDEO_INFO_FORMAT (vinfo) == GST_VIDEO_FORMAT_ENCODED) {
    /* Use native I420 format */
    encoder->n_components = 3;
    for (i = 0; i < encoder->n_components; ++i) {
      if (i == 0)
        encoder->h_samp[i] = encoder->v_samp[i] = 2;
      else
        encoder->h_samp[i] = encoder->v_samp[i] = 1;
      GST_DEBUG ("sampling factors: %d %d", encoder->h_samp[i],
          encoder->v_samp[i]);
    }
    return;
  }

  encoder->n_components = GST_VIDEO_INFO_N_COMPONENTS (vinfo);

  encoder->h_max_samp = 0;
  encoder->v_max_samp = 0;
  for (i = 0; i < encoder->n_components; ++i) {
    encoder->cwidth[i] = GST_VIDEO_INFO_COMP_WIDTH (vinfo, i);
    encoder->cheight[i] = GST_VIDEO_INFO_COMP_HEIGHT (vinfo, i);
    encoder->h_samp[i] =
        GST_ROUND_UP_4 (GST_VIDEO_INFO_WIDTH (vinfo)) / encoder->cwidth[i];
    encoder->h_max_samp = MAX (encoder->h_max_samp, encoder->h_samp[i]);
    encoder->v_samp[i] =
        GST_ROUND_UP_4 (GST_VIDEO_INFO_HEIGHT (vinfo)) / encoder->cheight[i];
    encoder->v_max_samp = MAX (encoder->v_max_samp, encoder->v_samp[i]);
  }
  /* samp should only be 1, 2 or 4 */
  g_assert (encoder->h_max_samp <= 4);
  g_assert (encoder->v_max_samp <= 4);

  /* now invert */
  /* maximum is invariant, as one of the components should have samp 1 */
  for (i = 0; i < encoder->n_components; ++i) {
    encoder->h_samp[i] = encoder->h_max_samp / encoder->h_samp[i];
    encoder->v_samp[i] = encoder->v_max_samp / encoder->v_samp[i];
    GST_DEBUG ("sampling factors: %d %d", encoder->h_samp[i],
        encoder->v_samp[i]);
  }
}
/* Reconfigures the encoder with the new properties */
static GstVaapiEncoderStatus
gst_vaapi_encoder_reconfigure_internal (GstVaapiEncoder * encoder)
{
  GstVaapiEncoderClass *const klass = GST_VAAPI_ENCODER_GET_CLASS (encoder);
  GstVideoInfo *const vip = GST_VAAPI_ENCODER_VIDEO_INFO (encoder);
  GstVaapiEncoderStatus status;
  GstVaapiVideoPool *pool;
  guint codedbuf_size;

  /* Generate a keyframe every second */
  if (!encoder->keyframe_period)
    encoder->keyframe_period = (vip->fps_n + vip->fps_d - 1) / vip->fps_d;

  status = klass->reconfigure (encoder);
  if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
    return status;

  if (!gst_vaapi_encoder_ensure_context (encoder))
    goto error_reset_context;

  codedbuf_size = encoder->codedbuf_pool ?
      gst_vaapi_coded_buffer_pool_get_buffer_size (GST_VAAPI_CODED_BUFFER_POOL
      (encoder)) : 0;
  if (codedbuf_size != encoder->codedbuf_size) {
    pool = gst_vaapi_coded_buffer_pool_new (encoder, encoder->codedbuf_size);
    if (!pool)
      goto error_alloc_codedbuf_pool;
    gst_vaapi_video_pool_set_capacity (pool, 5);
    gst_vaapi_video_pool_replace (&encoder->codedbuf_pool, pool);
    gst_vaapi_video_pool_unref (pool);
  }
  return GST_VAAPI_ENCODER_STATUS_SUCCESS;

  /* ERRORS */
error_alloc_codedbuf_pool:
  {
    GST_ERROR ("failed to initialize coded buffer pool");
    return GST_VAAPI_ENCODER_STATUS_ERROR_ALLOCATION_FAILED;
  }
error_reset_context:
  {
    GST_ERROR ("failed to update VA context");
    return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
  }
}
/* Updates video context */
static gboolean
set_context_info (GstVaapiEncoder * encoder)
{
  GstVaapiContextInfo *const cip = &encoder->context_info;
  GstVaapiConfigInfoEncoder *const config = &cip->config.encoder;
  const GstVideoFormat format =
    GST_VIDEO_INFO_FORMAT (GST_VAAPI_ENCODER_VIDEO_INFO (encoder));
  const GstVaapiEncoderClassData *const cdata =
      GST_VAAPI_ENCODER_GET_CLASS (encoder)->class_data;

  cip->usage = GST_VAAPI_CONTEXT_USAGE_ENCODE;
  cip->profile = encoder->profile;
  cip->entrypoint = GST_VAAPI_ENTRYPOINT_SLICE_ENCODE;
  if (cdata->codec != GST_VAAPI_CODEC_JPEG)
    cip->entrypoint = GST_VAAPI_ENTRYPOINT_SLICE_ENCODE;
  else
    cip->entrypoint = GST_VAAPI_ENTRYPOINT_PICTURE_ENCODE;
  cip->chroma_type = gst_vaapi_video_format_get_chroma_type (format);
  cip->width = GST_VAAPI_ENCODER_WIDTH (encoder);
  cip->height = GST_VAAPI_ENCODER_HEIGHT (encoder);
  cip->ref_frames = encoder->num_ref_frames;

  if (!cip->chroma_type  && (format != GST_VIDEO_FORMAT_ENCODED))
    goto error_unsupported_format;

  if (cip->chroma_type != GST_VAAPI_CHROMA_TYPE_YUV420 &&
      format != GST_VIDEO_FORMAT_ENCODED) {
    GST_ERROR ("We are only supporting YUV:4:2:0 for encoding,"
        "please try to use vaapipostproc to convert the input format!");
    goto error_unsupported_format;
  }
  
  memset (config, 0, sizeof (*config));
  config->rc_mode = GST_VAAPI_ENCODER_RATE_CONTROL (encoder);
  config->packed_headers = get_packed_headers (encoder);
  return TRUE;

  /* ERRORS */
error_unsupported_format:
  {
    GST_ERROR ("failed to determine chroma type for format %s",
        gst_vaapi_video_format_to_string (format));
    return FALSE;
  }
}
static GstVaapiEncoderStatus
set_context_info (GstVaapiEncoder * base_encoder)
{
  GstVaapiEncoderMpeg2 *const encoder =
      GST_VAAPI_ENCODER_MPEG2_CAST (base_encoder);
  GstVideoInfo *const vip = GST_VAAPI_ENCODER_VIDEO_INFO (encoder);

  /* Maximum sizes for common headers (in bytes) */
  enum
  {
    MAX_SEQ_HDR_SIZE = 140,
    MAX_SEQ_EXT_SIZE = 10,
    MAX_GOP_SIZE = 8,
    MAX_PIC_HDR_SIZE = 10,
    MAX_PIC_EXT_SIZE = 11,
    MAX_SLICE_HDR_SIZE = 8,
  };

  if (!ensure_hw_profile (encoder))
    return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_PROFILE;

  base_encoder->num_ref_frames = 2;

  /* Only YUV 4:2:0 formats are supported for now. This means that we
     have a limit of 4608 bits per macroblock. */
  base_encoder->codedbuf_size = (GST_ROUND_UP_16 (vip->width) *
      GST_ROUND_UP_16 (vip->height) / 256) * 576;

  /* Account for Sequence, GOP, and Picture headers */
  /* XXX: exclude unused Sequence Display Extension, Sequence Scalable
     Extension, Quantization Matrix Extension, Picture Display Extension,
     Picture Temporal Scalable Extension, Picture Spatial Scalable
     Extension */
  base_encoder->codedbuf_size += MAX_SEQ_HDR_SIZE + MAX_SEQ_EXT_SIZE +
      MAX_GOP_SIZE + MAX_PIC_HDR_SIZE + MAX_PIC_EXT_SIZE;

  /* Account for Slice headers. We use one slice per line of macroblock */
  base_encoder->codedbuf_size += (GST_ROUND_UP_16 (vip->height) / 16) *
      MAX_SLICE_HDR_SIZE;

  return GST_VAAPI_ENCODER_STATUS_SUCCESS;
}
static GstVaapiEncoderStatus
set_context_info (GstVaapiEncoder * base_encoder)
{
  GstVaapiEncoderVP8 *encoder = GST_VAAPI_ENCODER_VP8_CAST (base_encoder);
  GstVideoInfo *const vip = GST_VAAPI_ENCODER_VIDEO_INFO (encoder);

  /* Maximum sizes for common headers (in bytes) */
  enum
  {
    MAX_FRAME_TAG_SIZE = 10,
    MAX_UPDATE_SEGMENTATION_SIZE = 13,
    MAX_MB_LF_ADJUSTMENTS_SIZE = 9,
    MAX_QUANT_INDICES_SIZE = 5,
    MAX_TOKEN_PROB_UPDATE_SIZE = 1188,
    MAX_MV_PROBE_UPDATE_SIZE = 38,
    MAX_REST_OF_FRAME_HDR_SIZE = 15
  };

  if (!ensure_hw_profile (encoder))
    return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_PROFILE;

  base_encoder->num_ref_frames = 3;

  /* Only YUV 4:2:0 formats are supported for now. */
  /* Assumig 4 times compression ratio */
  base_encoder->codedbuf_size = GST_ROUND_UP_16 (vip->width) *
      GST_ROUND_UP_16 (vip->height) * 12 / 4;

  base_encoder->codedbuf_size +=
      MAX_FRAME_TAG_SIZE + MAX_UPDATE_SEGMENTATION_SIZE +
      MAX_MB_LF_ADJUSTMENTS_SIZE + MAX_QUANT_INDICES_SIZE +
      MAX_TOKEN_PROB_UPDATE_SIZE + MAX_MV_PROBE_UPDATE_SIZE +
      MAX_REST_OF_FRAME_HDR_SIZE;

  return GST_VAAPI_ENCODER_STATUS_SUCCESS;
}
/* Derives the minimum level from the current configuration */
static gboolean
ensure_level (GstVaapiEncoderMpeg2 * encoder)
{
  const GstVideoInfo *const vip = GST_VAAPI_ENCODER_VIDEO_INFO (encoder);
  const guint fps = (vip->fps_n + vip->fps_d - 1) / vip->fps_d;
  const guint bitrate = GST_VAAPI_ENCODER_CAST (encoder)->bitrate;
  const GstVaapiMPEG2LevelLimits *limits_table;
  guint i, num_limits, num_samples;

  num_samples = gst_util_uint64_scale_int_ceil (vip->width * vip->height,
      vip->fps_n, vip->fps_d);

  limits_table = gst_vaapi_utils_mpeg2_get_level_limits_table (&num_limits);
  for (i = 0; i < num_limits; i++) {
    const GstVaapiMPEG2LevelLimits *const limits = &limits_table[i];
    if (vip->width <= limits->horizontal_size_value &&
        vip->height <= limits->vertical_size_value &&
        fps <= limits->frame_rate_value &&
        num_samples <= limits->sample_rate &&
        (!bitrate || bitrate <= limits->bit_rate))
      break;
  }
  if (i == num_limits)
    goto error_unsupported_level;

  encoder->level = limits_table[i].level;
  encoder->level_idc = limits_table[i].level_idc;
  return TRUE;

  /* ERRORS */
error_unsupported_level:
  {
    GST_ERROR ("failed to find a suitable level matching codec config");
    return FALSE;
  }
}