static GstVaapiDecoderStatus decode_picture (GstVaapiDecoderJpeg * decoder, GstJpegSegment * seg) { GstVaapiDecoderJpegPrivate *const priv = &decoder->priv; GstJpegFrameHdr *const frame_hdr = &priv->frame_hdr; if (!VALID_STATE (decoder, GOT_SOI)) return GST_VAAPI_DECODER_STATUS_SUCCESS; switch (seg->marker) { case GST_JPEG_MARKER_SOF_MIN: priv->profile = GST_VAAPI_PROFILE_JPEG_BASELINE; break; default: GST_ERROR ("unsupported profile %d", seg->marker); return GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_PROFILE; } memset (frame_hdr, 0, sizeof (*frame_hdr)); if (!gst_jpeg_segment_parse_frame_header (seg, frame_hdr)) { GST_ERROR ("failed to parse image"); return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER; } if (priv->height != frame_hdr->height || priv->width != frame_hdr->width) priv->size_changed = TRUE; priv->height = frame_hdr->height; priv->width = frame_hdr->width; priv->decoder_state |= GST_JPEG_VIDEO_STATE_GOT_SOF; return GST_VAAPI_DECODER_STATUS_SUCCESS; }
static GstVaapiDecoderStatus decode_current_picture (GstVaapiDecoderJpeg * decoder) { GstVaapiDecoderJpegPrivate *const priv = &decoder->priv; GstVaapiPicture *const picture = priv->current_picture; if (!VALID_STATE (decoder, VALID_PICTURE)) goto drop_frame; priv->decoder_state = 0; if (!picture) return GST_VAAPI_DECODER_STATUS_SUCCESS; if (!gst_vaapi_picture_decode (picture)) goto error; if (!gst_vaapi_picture_output (picture)) goto error; gst_vaapi_picture_replace (&priv->current_picture, NULL); return GST_VAAPI_DECODER_STATUS_SUCCESS; /* ERRORS */ error: { gst_vaapi_picture_replace (&priv->current_picture, NULL); return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN; } drop_frame: { priv->decoder_state = 0; return (GstVaapiDecoderStatus) GST_VAAPI_DECODER_STATUS_DROP_FRAME; } }
/* Update the state machine */ void door_update_state (void) { new_event = get_new_event (); if (VALID_STATE(door_current_state) && VALID_EVENT(new_event)) { door_state_table [door_current_state][new_event] (); } }
static GstVaapiDecoderStatus decode_restart_interval (GstVaapiDecoderJpeg * decoder, GstJpegSegment * seg) { GstVaapiDecoderJpegPrivate *const priv = &decoder->priv; if (!VALID_STATE (decoder, GOT_SOI)) return GST_VAAPI_DECODER_STATUS_SUCCESS; if (!gst_jpeg_segment_parse_restart_interval (seg, &priv->mcu_restart)) { GST_ERROR ("failed to parse restart interval"); return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER; } return GST_VAAPI_DECODER_STATUS_SUCCESS; }
static GstVaapiDecoderStatus decode_quant_table (GstVaapiDecoderJpeg * decoder, GstJpegSegment * seg) { GstVaapiDecoderJpegPrivate *const priv = &decoder->priv; if (!VALID_STATE (decoder, GOT_SOI)) return GST_VAAPI_DECODER_STATUS_SUCCESS; if (!gst_jpeg_segment_parse_quantization_table (seg, &priv->quant_tables)) { GST_ERROR ("failed to parse quantization table"); return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER; } priv->decoder_state |= GST_JPEG_VIDEO_STATE_GOT_IQ_TABLE; return GST_VAAPI_DECODER_STATUS_SUCCESS; }
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 GstVaapiDecoderStatus gst_vaapi_decoder_jpeg_start_frame (GstVaapiDecoder * base_decoder, GstVaapiDecoderUnit * base_unit) { GstVaapiDecoderJpeg *const decoder = GST_VAAPI_DECODER_JPEG_CAST (base_decoder); GstVaapiDecoderJpegPrivate *const priv = &decoder->priv; GstVaapiPicture *picture; GstVaapiDecoderStatus status; if (!VALID_STATE (decoder, GOT_SOF)) return GST_VAAPI_DECODER_STATUS_SUCCESS; status = ensure_context (decoder); if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) { GST_ERROR ("failed to reset context"); return status; } picture = GST_VAAPI_PICTURE_NEW (JPEGBaseline, decoder); if (!picture) { GST_ERROR ("failed to allocate picture"); return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED; } gst_vaapi_picture_replace (&priv->current_picture, picture); gst_vaapi_picture_unref (picture); if (!fill_picture (decoder, picture, &priv->frame_hdr)) return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN; status = fill_quantization_table (decoder, picture); if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) return status; /* Update presentation time */ picture->pts = GST_VAAPI_DECODER_CODEC_FRAME (decoder)->pts; return GST_VAAPI_DECODER_STATUS_SUCCESS; }
static GstVaapiDecoderStatus gst_vaapi_decoder_jpeg_parse (GstVaapiDecoder * base_decoder, GstAdapter * adapter, gboolean at_eos, GstVaapiDecoderUnit * unit) { GstVaapiDecoderJpeg *const decoder = GST_VAAPI_DECODER_JPEG_CAST (base_decoder); GstVaapiDecoderJpegPrivate *const priv = &decoder->priv; GstVaapiParserState *const ps = GST_VAAPI_PARSER_STATE (base_decoder); GstVaapiDecoderStatus status; GstJpegMarker marker; GstJpegSegment seg; const guchar *buf; guint buf_size, flags; gint ofs1, ofs2; status = ensure_decoder (decoder); if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) return status; /* Expect at least 2 bytes for the marker */ buf_size = gst_adapter_available (adapter); if (buf_size < 2) return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA; buf = gst_adapter_map (adapter, buf_size); if (!buf) return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA; ofs1 = ps->input_offset1 - 2; if (ofs1 < 0) ofs1 = 0; for (;;) { // Skip any garbage until we reach SOI, if needed if (!gst_jpeg_parse (&seg, buf, buf_size, ofs1)) { gst_adapter_unmap (adapter); ps->input_offset1 = buf_size; return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA; } ofs1 = seg.offset; marker = seg.marker; if (!VALID_STATE (parser, GOT_SOI) && marker != GST_JPEG_MARKER_SOI) continue; if (marker == GST_JPEG_MARKER_SOS) { ofs2 = ps->input_offset2 - 2; if (ofs2 < ofs1 + seg.size) ofs2 = ofs1 + seg.size; // Parse the whole scan + ECSs, including RSTi for (;;) { if (!gst_jpeg_parse (&seg, buf, buf_size, ofs2)) { gst_adapter_unmap (adapter); ps->input_offset1 = ofs1; ps->input_offset2 = buf_size; return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA; } if (is_scan_complete (seg.marker)) break; ofs2 = seg.offset + seg.size; } ofs2 = seg.offset - 2; } else { // Check that the whole segment is actually available (in buffer) ofs2 = ofs1 + seg.size; if (ofs2 > buf_size) { gst_adapter_unmap (adapter); ps->input_offset1 = ofs1; return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA; } } break; } gst_adapter_unmap (adapter); unit->size = ofs2 - ofs1; unit_set_marker_code (unit, marker); gst_adapter_flush (adapter, ofs1); ps->input_offset1 = 2; ps->input_offset2 = 2; flags = 0; switch (marker) { case GST_JPEG_MARKER_SOI: flags |= GST_VAAPI_DECODER_UNIT_FLAG_FRAME_START; priv->parser_state |= GST_JPEG_VIDEO_STATE_GOT_SOI; break; case GST_JPEG_MARKER_EOI: flags |= GST_VAAPI_DECODER_UNIT_FLAG_FRAME_END; priv->parser_state = 0; break; case GST_JPEG_MARKER_SOS: flags |= GST_VAAPI_DECODER_UNIT_FLAG_SLICE; priv->parser_state |= GST_JPEG_VIDEO_STATE_GOT_SOS; break; case GST_JPEG_MARKER_DAC: case GST_JPEG_MARKER_DHT: case GST_JPEG_MARKER_DQT: if (priv->parser_state & GST_JPEG_VIDEO_STATE_GOT_SOF) flags |= GST_VAAPI_DECODER_UNIT_FLAG_SLICE; break; case GST_JPEG_MARKER_DRI: if (priv->parser_state & GST_JPEG_VIDEO_STATE_GOT_SOS) flags |= GST_VAAPI_DECODER_UNIT_FLAG_SLICE; break; case GST_JPEG_MARKER_DNL: flags |= GST_VAAPI_DECODER_UNIT_FLAG_SLICE; break; case GST_JPEG_MARKER_COM: flags |= GST_VAAPI_DECODER_UNIT_FLAG_SKIP; break; default: /* SOFn segments */ if (marker >= GST_JPEG_MARKER_SOF_MIN && marker <= GST_JPEG_MARKER_SOF_MAX) priv->parser_state |= GST_JPEG_VIDEO_STATE_GOT_SOF; /* Application segments */ else if (marker >= GST_JPEG_MARKER_APP_MIN && marker <= GST_JPEG_MARKER_APP_MAX) flags |= GST_VAAPI_DECODER_UNIT_FLAG_SKIP; /* Reserved */ else if (marker >= 0x02 && marker <= 0xbf) flags |= GST_VAAPI_DECODER_UNIT_FLAG_SKIP; break; } GST_VAAPI_DECODER_UNIT_FLAG_SET (unit, flags); return GST_VAAPI_DECODER_STATUS_SUCCESS; }
static GstVaapiDecoderStatus decode_scan (GstVaapiDecoderJpeg * decoder, GstJpegSegment * seg) { GstVaapiDecoderJpegPrivate *const priv = &decoder->priv; GstVaapiPicture *const picture = priv->current_picture; GstVaapiSlice *slice; VASliceParameterBufferJPEGBaseline *slice_param; GstJpegScanHdr scan_hdr; guint scan_hdr_size, scan_data_size; guint i, h_max, v_max, mcu_width, mcu_height; if (!VALID_STATE (decoder, GOT_SOF)) return GST_VAAPI_DECODER_STATUS_SUCCESS; scan_hdr_size = (seg->data[seg->offset] << 8) | seg->data[seg->offset + 1]; scan_data_size = seg->size - scan_hdr_size; memset (&scan_hdr, 0, sizeof (scan_hdr)); if (!gst_jpeg_segment_parse_scan_header (seg, &scan_hdr)) { GST_ERROR ("failed to parse scan header"); return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER; } slice = GST_VAAPI_SLICE_NEW (JPEGBaseline, decoder, seg->data + seg->offset + scan_hdr_size, scan_data_size); if (!slice) { GST_ERROR ("failed to allocate slice"); return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED; } gst_vaapi_picture_add_slice (picture, slice); if (!VALID_STATE (decoder, GOT_HUF_TABLE)) gst_jpeg_get_default_huffman_tables (&priv->huf_tables); // Update VA Huffman table if it changed for this scan if (huffman_tables_updated (&priv->huf_tables)) { slice->huf_table = GST_VAAPI_HUFFMAN_TABLE_NEW (JPEGBaseline, decoder); if (!slice->huf_table) { GST_ERROR ("failed to allocate Huffman tables"); huffman_tables_reset (&priv->huf_tables); return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED; } fill_huffman_table (slice->huf_table, &priv->huf_tables); huffman_tables_reset (&priv->huf_tables); } slice_param = slice->param; slice_param->num_components = scan_hdr.num_components; for (i = 0; i < scan_hdr.num_components; i++) { slice_param->components[i].component_selector = scan_hdr.components[i].component_selector; slice_param->components[i].dc_table_selector = scan_hdr.components[i].dc_selector; slice_param->components[i].ac_table_selector = scan_hdr.components[i].ac_selector; } slice_param->restart_interval = priv->mcu_restart; slice_param->slice_horizontal_position = 0; slice_param->slice_vertical_position = 0; get_max_sampling_factors (&priv->frame_hdr, &h_max, &v_max); mcu_width = 8 * h_max; mcu_height = 8 * v_max; if (scan_hdr.num_components == 1) { // Non-interleaved const guint Csj = slice_param->components[0].component_selector; const GstJpegFrameComponent *const fcp = get_component (&priv->frame_hdr, Csj); if (!fcp || fcp->horizontal_factor == 0 || fcp->vertical_factor == 0) { GST_ERROR ("failed to validate image component %u", Csj); return GST_VAAPI_DECODER_STATUS_ERROR_INVALID_PARAMETER; } mcu_width /= fcp->horizontal_factor; mcu_height /= fcp->vertical_factor; } slice_param->num_mcus = ((priv->frame_hdr.width + mcu_width - 1) / mcu_width) * ((priv->frame_hdr.height + mcu_height - 1) / mcu_height); priv->decoder_state |= GST_JPEG_VIDEO_STATE_GOT_SOS; return GST_VAAPI_DECODER_STATUS_SUCCESS; }