static GstVaapiDecoderStatus
fill_quantization_table (GstVaapiDecoderJpeg * decoder,
    GstVaapiPicture * picture)
{
  GstVaapiDecoderJpegPrivate *const priv = &decoder->priv;
  VAIQMatrixBufferJPEGBaseline *iq_matrix;
  guint i, j, num_tables;

  if (!VALID_STATE (decoder, GOT_IQ_TABLE))
    gst_jpeg_get_default_quantization_tables (&priv->quant_tables);

  picture->iq_matrix = GST_VAAPI_IQ_MATRIX_NEW (JPEGBaseline, decoder);
  if (!picture->iq_matrix) {
    GST_ERROR ("failed to allocate quantiser table");
    return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
  }
  iq_matrix = picture->iq_matrix->param;

  num_tables = MIN (G_N_ELEMENTS (iq_matrix->quantiser_table),
      GST_JPEG_MAX_QUANT_ELEMENTS);

  for (i = 0; i < num_tables; i++) {
    GstJpegQuantTable *const quant_table = &priv->quant_tables.quant_tables[i];

    iq_matrix->load_quantiser_table[i] = quant_table->valid;
    if (!iq_matrix->load_quantiser_table[i])
      continue;

    if (quant_table->quant_precision != 0) {
      // Only Baseline profile is supported, thus 8-bit Qk values
      GST_ERROR ("unsupported quantization table element precision");
      return GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_CHROMA_FORMAT;
    }

    for (j = 0; j < GST_JPEG_MAX_QUANT_ELEMENTS; j++)
      iq_matrix->quantiser_table[i][j] = quant_table->quant_table[j];
    iq_matrix->load_quantiser_table[i] = 1;
    quant_table->valid = FALSE;
  }
  return GST_VAAPI_DECODER_STATUS_SUCCESS;
}
static gboolean
fill_quantization_table (GstVaapiEncoderJpeg * encoder,
    GstVaapiEncPicture * picture)
{
  VAQMatrixBufferJPEG *q_matrix;
  int i;

  g_assert (picture);

  picture->q_matrix = GST_VAAPI_ENC_Q_MATRIX_NEW (JPEG, encoder);
  if (!picture->q_matrix) {
    GST_ERROR ("failed to allocate quantiser table");
    return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
  }
  q_matrix = picture->q_matrix->param;

  if (!encoder->has_quant_tables) {
    gst_jpeg_get_default_quantization_tables (&encoder->quant_tables);
    encoder->has_quant_tables = TRUE;
    generate_scaled_qm (&encoder->quant_tables, &encoder->scaled_quant_tables,
        encoder->quality);
  }
  q_matrix->load_lum_quantiser_matrix = 1;
  for (i = 0; i < GST_JPEG_MAX_QUANT_ELEMENTS; i++) {
    q_matrix->lum_quantiser_matrix[i] =
        encoder->quant_tables.quant_tables[0].quant_table[i];
  }

  q_matrix->load_chroma_quantiser_matrix = 1;
  for (i = 0; i < GST_JPEG_MAX_QUANT_ELEMENTS; i++) {
    q_matrix->chroma_quantiser_matrix[i] =
        encoder->quant_tables.quant_tables[1].quant_table[i];
  }

  return TRUE;
}
static gboolean
fill_quantization_table(
    GstVaapiDecoderJpeg *decoder, 
    GstVaapiPicture     *picture
)
{
    GstVaapiDecoderJpegPrivate * const priv = decoder->priv;
    VAIQMatrixBufferJPEGBaseline *iq_matrix;
    guint i, j, num_tables;

    if (!priv->has_quant_table)
        gst_jpeg_get_default_quantization_tables(&priv->quant_tables);
    
    picture->iq_matrix = GST_VAAPI_IQ_MATRIX_NEW(JPEGBaseline, decoder);
    g_assert(picture->iq_matrix);
    iq_matrix = picture->iq_matrix->param;

    num_tables = MIN(G_N_ELEMENTS(iq_matrix->quantiser_table),
                     GST_JPEG_MAX_QUANT_ELEMENTS);

    for (i = 0; i < num_tables; i++) {
        GstJpegQuantTable * const quant_table =
            &priv->quant_tables.quant_tables[i];

        iq_matrix->load_quantiser_table[i] = quant_table->valid;
        if (!iq_matrix->load_quantiser_table[i])
            continue;

        g_assert(quant_table->quant_precision == 0);
        for (j = 0; j < GST_JPEG_MAX_QUANT_ELEMENTS; j++)
            iq_matrix->quantiser_table[i][j] = quant_table->quant_table[j];
        iq_matrix->load_quantiser_table[i] = 1;
        quant_table->valid = FALSE;
    }
    return TRUE;
}
static gboolean
bs_write_jpeg_header (GstBitWriter * bs, GstVaapiEncoderJpeg * encoder,
    GstVaapiEncPicture * picture)
{
  GstJpegFrameHdr frame_hdr;
  GstJpegScanHdr scan_hdr;
  guint i, j;

  gst_bit_writer_put_bits_uint8 (bs, 0xFF, 8);
  gst_bit_writer_put_bits_uint8 (bs, GST_JPEG_MARKER_SOI, 8);
  gst_bit_writer_put_bits_uint8 (bs, 0xFF, 8);
  gst_bit_writer_put_bits_uint8 (bs, GST_JPEG_MARKER_APP_MIN, 8);
  gst_bit_writer_put_bits_uint16 (bs, 16, 16);
  gst_bit_writer_put_bits_uint8 (bs, 0x4A, 8);  //J
  gst_bit_writer_put_bits_uint8 (bs, 0x46, 8);  //F
  gst_bit_writer_put_bits_uint8 (bs, 0x49, 8);  //I
  gst_bit_writer_put_bits_uint8 (bs, 0x46, 8);  //F
  gst_bit_writer_put_bits_uint8 (bs, 0x00, 8);  //0
  gst_bit_writer_put_bits_uint8 (bs, 1, 8);     //Major Version
  gst_bit_writer_put_bits_uint8 (bs, 1, 8);     //Minor Version
  gst_bit_writer_put_bits_uint8 (bs, 0, 8);     //Density units 0:no units, 1:pixels per inch, 2: pixels per cm
  gst_bit_writer_put_bits_uint16 (bs, 1, 16);   //X density (pixel-aspect-ratio)
  gst_bit_writer_put_bits_uint16 (bs, 1, 16);   //Y density (pixel-aspect-ratio)
  gst_bit_writer_put_bits_uint8 (bs, 0, 8);     //Thumbnail width
  gst_bit_writer_put_bits_uint8 (bs, 0, 8);     //Thumbnail height

  /* Add  quantization table */
  if (!encoder->has_quant_tables) {
    gst_jpeg_get_default_quantization_tables (&encoder->quant_tables);
    generate_scaled_qm (&encoder->quant_tables, &encoder->scaled_quant_tables,
        encoder->quality);
    encoder->has_quant_tables = TRUE;
  }

  gst_bit_writer_put_bits_uint8 (bs, 0xFF, 8);
  gst_bit_writer_put_bits_uint8 (bs, GST_JPEG_MARKER_DQT, 8);
  gst_bit_writer_put_bits_uint16 (bs, 3 + GST_JPEG_MAX_QUANT_ELEMENTS, 16);     //Lq
  gst_bit_writer_put_bits_uint8 (bs, encoder->quant_tables.quant_tables[0].quant_precision, 4); //Pq
  gst_bit_writer_put_bits_uint8 (bs, 0, 4);     //Tq
  for (i = 0; i < GST_JPEG_MAX_QUANT_ELEMENTS; i++) {
    gst_bit_writer_put_bits_uint16 (bs,
        encoder->scaled_quant_tables.quant_tables[0].quant_table[i], 8);
  }
  gst_bit_writer_put_bits_uint8 (bs, 0xFF, 8);
  gst_bit_writer_put_bits_uint8 (bs, GST_JPEG_MARKER_DQT, 8);
  gst_bit_writer_put_bits_uint16 (bs, 3 + GST_JPEG_MAX_QUANT_ELEMENTS, 16);     //Lq
  gst_bit_writer_put_bits_uint8 (bs, encoder->quant_tables.quant_tables[1].quant_precision, 4); //Pq
  gst_bit_writer_put_bits_uint8 (bs, 1, 4);     //Tq
  for (i = 0; i < GST_JPEG_MAX_QUANT_ELEMENTS; i++) {
    gst_bit_writer_put_bits_uint16 (bs,
        encoder->scaled_quant_tables.quant_tables[1].quant_table[i], 8);
  }

  /*Add frame header */
  generate_frame_hdr (&frame_hdr, encoder, picture);
  gst_bit_writer_put_bits_uint8 (bs, 0xFF, 8);
  gst_bit_writer_put_bits_uint8 (bs, GST_JPEG_MARKER_SOF_MIN, 8);
  gst_bit_writer_put_bits_uint16 (bs, 8 + (3 * 3), 16); //lf, Size of FrameHeader in bytes without the Marker SOF
  gst_bit_writer_put_bits_uint8 (bs, frame_hdr.sample_precision, 8);
  gst_bit_writer_put_bits_uint16 (bs, frame_hdr.height, 16);
  gst_bit_writer_put_bits_uint16 (bs, frame_hdr.width, 16);
  gst_bit_writer_put_bits_uint8 (bs, frame_hdr.num_components, 8);
  for (i = 0; i < frame_hdr.num_components; i++) {
    gst_bit_writer_put_bits_uint8 (bs, frame_hdr.components[i].identifier, 8);
    gst_bit_writer_put_bits_uint8 (bs,
        frame_hdr.components[i].horizontal_factor, 4);
    gst_bit_writer_put_bits_uint8 (bs, frame_hdr.components[i].vertical_factor,
        4);
    gst_bit_writer_put_bits_uint8 (bs,
        frame_hdr.components[i].quant_table_selector, 8);
  }

  /* Add Huffman table */
  if (!encoder->has_huff_tables) {
    gst_jpeg_get_default_huffman_tables (&encoder->huff_tables);
    encoder->has_huff_tables = TRUE;
  }
  for (i = 0; i < 2; i++) {
    gst_bit_writer_put_bits_uint8 (bs, 0xFF, 8);
    gst_bit_writer_put_bits_uint8 (bs, GST_JPEG_MARKER_DHT, 8);
    gst_bit_writer_put_bits_uint16 (bs, 0x1F, 16);      //length of table
    gst_bit_writer_put_bits_uint8 (bs, 0, 4);
    gst_bit_writer_put_bits_uint8 (bs, i, 4);
    for (j = 0; j < NUM_DC_RUN_SIZE_BITS; j++) {
      gst_bit_writer_put_bits_uint8 (bs,
          encoder->huff_tables.dc_tables[i].huf_bits[j], 8);
    }

    for (j = 0; j < NUM_DC_CODE_WORDS_HUFFVAL; j++) {
      gst_bit_writer_put_bits_uint8 (bs,
          encoder->huff_tables.dc_tables[i].huf_values[j], 8);
    }

    gst_bit_writer_put_bits_uint8 (bs, 0xFF, 8);
    gst_bit_writer_put_bits_uint8 (bs, GST_JPEG_MARKER_DHT, 8);
    gst_bit_writer_put_bits_uint16 (bs, 0xB5, 16);      //length of table
    gst_bit_writer_put_bits_uint8 (bs, 1, 4);
    gst_bit_writer_put_bits_uint8 (bs, i, 4);
    for (j = 0; j < NUM_AC_RUN_SIZE_BITS; j++) {
      gst_bit_writer_put_bits_uint8 (bs,
          encoder->huff_tables.ac_tables[i].huf_bits[j], 8);
    }

    for (j = 0; j < NUM_AC_CODE_WORDS_HUFFVAL; j++) {
      gst_bit_writer_put_bits_uint8 (bs,
          encoder->huff_tables.ac_tables[i].huf_values[j], 8);
    }
  }

  /* Add ScanHeader */
  generate_scan_hdr (&scan_hdr, picture);
  gst_bit_writer_put_bits_uint8 (bs, 0xFF, 8);
  gst_bit_writer_put_bits_uint8 (bs, GST_JPEG_MARKER_SOS, 8);
  gst_bit_writer_put_bits_uint16 (bs, 12, 16);  //Length of Scan
  gst_bit_writer_put_bits_uint8 (bs, scan_hdr.num_components, 8);

  for (i = 0; i < scan_hdr.num_components; i++) {
    gst_bit_writer_put_bits_uint8 (bs,
        scan_hdr.components[i].component_selector, 8);
    gst_bit_writer_put_bits_uint8 (bs, scan_hdr.components[i].dc_selector, 4);
    gst_bit_writer_put_bits_uint8 (bs, scan_hdr.components[i].ac_selector, 4);
  }
  gst_bit_writer_put_bits_uint8 (bs, 0, 8);     //0 for Baseline
  gst_bit_writer_put_bits_uint8 (bs, 63, 8);    //63 for Baseline
  gst_bit_writer_put_bits_uint8 (bs, 0, 4);     //0 for Baseline
  gst_bit_writer_put_bits_uint8 (bs, 0, 4);     //0 for Baseline

  return TRUE;
}