jpeg: rework and optimize parser.

Split the input buffer data into decoder units that represent a JPEG
segment. Handle scan decoder unit specifically so that it can include
both the scan header (SOS) but also any other ECS or RSTi segment.

That way, we parse the input buffer stream only once at the gst-vaapi
level instead of (i) in gst_vaapi_decoder_jpeg_parse() to split the
stream into frames SOI .. EOI and (ii) in decode_buffer() to further
determine segment boundaries and decode them.

In practice, this is a +15 to +25% performance improvement.
This commit is contained in:
Gwenole Beauchesne 2013-09-20 10:12:08 +02:00
parent a4aac6b89e
commit f52dfe5797
3 changed files with 235 additions and 240 deletions

View file

@ -85,6 +85,7 @@ parser_state_prepare(GstVaapiParserState *ps, GstAdapter *adapter)
reset:
ps->current_adapter = adapter;
ps->input_offset1 = -1;
ps->input_offset2 = -1;
}

View file

@ -60,6 +60,7 @@ struct _GstVaapiDecoderJpegPrivate {
guint mcu_restart;
guint is_opened : 1;
guint profile_changed : 1;
guint expect_SOI : 1;
};
/**
@ -83,14 +84,17 @@ struct _GstVaapiDecoderJpegClass {
GstVaapiDecoderClass parent_class;
};
typedef struct _GstJpegScanSegment GstJpegScanSegment;
struct _GstJpegScanSegment {
guint header_offset;
guint header_size;
guint data_offset;
guint data_size;
guint is_valid : 1;
};
static inline void
unit_set_marker_code(GstVaapiDecoderUnit *unit, GstJpegMarkerCode marker)
{
unit->parsed_info = GSIZE_TO_POINTER(marker);
}
static inline GstJpegMarkerCode
unit_get_marker_code(GstVaapiDecoderUnit *unit)
{
return GPOINTER_TO_SIZE(unit->parsed_info);
}
static void
gst_vaapi_decoder_jpeg_close(GstVaapiDecoderJpeg *decoder)
@ -110,8 +114,11 @@ gst_vaapi_decoder_jpeg_close(GstVaapiDecoderJpeg *decoder)
static gboolean
gst_vaapi_decoder_jpeg_open(GstVaapiDecoderJpeg *decoder)
{
GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
gst_vaapi_decoder_jpeg_close(decoder);
priv->expect_SOI = TRUE;
return TRUE;
}
@ -182,21 +189,25 @@ ensure_context(GstVaapiDecoderJpeg *decoder)
return GST_VAAPI_DECODER_STATUS_SUCCESS;
}
static gboolean
static GstVaapiDecoderStatus
decode_current_picture(GstVaapiDecoderJpeg *decoder)
{
GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
GstVaapiPicture * const picture = priv->current_picture;
gboolean success = TRUE;
if (picture) {
if (!gst_vaapi_picture_decode(picture))
success = FALSE;
else if (!gst_vaapi_picture_output(picture))
success = FALSE;
gst_vaapi_picture_replace(&priv->current_picture, NULL);
}
return success;
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;
error:
gst_vaapi_picture_replace(&priv->current_picture, NULL);
return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
}
static gboolean
@ -339,57 +350,28 @@ get_max_vertical_samples(GstJpegFrameHdr *frame_hdr)
}
static GstVaapiDecoderStatus
decode_picture(
GstVaapiDecoderJpeg *decoder,
guint8 profile,
const guchar *buf,
guint buf_size
)
decode_picture(GstVaapiDecoderJpeg *decoder, GstJpegMarkerSegment *seg,
const guchar *buf)
{
GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
GstJpegFrameHdr * const frame_hdr = &priv->frame_hdr;
GstVaapiPicture *picture;
GstVaapiDecoderStatus status;
switch (profile) {
switch (seg->marker) {
case GST_JPEG_MARKER_SOF_MIN:
priv->profile = GST_VAAPI_PROFILE_JPEG_BASELINE;
break;
default:
GST_ERROR("unsupported profile %d", profile);
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_parse_frame_hdr(frame_hdr, buf, buf_size, 0)) {
if (!gst_jpeg_parse_frame_hdr(frame_hdr, buf + seg->offset, seg->size, 0)) {
GST_ERROR("failed to parse image");
return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
}
priv->height = frame_hdr->height;
priv->width = frame_hdr->width;
status = ensure_context(decoder);
if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) {
GST_ERROR("failed to reset context");
return status;
}
if (priv->current_picture && !decode_current_picture(decoder))
return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
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, frame_hdr))
return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
/* Update presentation time */
picture->pts = GST_VAAPI_DECODER_CODEC_FRAME(decoder)->pts;
return GST_VAAPI_DECODER_STATUS_SUCCESS;
}
@ -444,47 +426,35 @@ decode_restart_interval(
}
static GstVaapiDecoderStatus
decode_scan(
GstVaapiDecoderJpeg *decoder,
const guchar *scan_header,
guint scan_header_size,
const guchar *scan_data,
guint scan_data_size
)
decode_scan(GstVaapiDecoderJpeg *decoder, GstJpegMarkerSegment *seg,
const guchar *buf)
{
GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
GstVaapiPicture *picture = priv->current_picture;
GstVaapiPicture * const picture = priv->current_picture;
GstVaapiSlice *slice;
VASliceParameterBufferJPEGBaseline *slice_param;
GstVaapiSlice *gst_slice;
guint total_h_samples, total_v_samples;
GstJpegScanHdr scan_hdr;
guint i;
GstJpegScanHdr scan_hdr;
guint scan_hdr_size, scan_data_size;
guint i, total_h_samples, total_v_samples;
if (!picture) {
GST_ERROR("There is no VAPicture before decoding scan.");
return GST_VAAPI_DECODER_STATUS_ERROR_INVALID_SURFACE;
}
if (!fill_quantization_table(decoder, picture)) {
GST_ERROR("failed to fill in quantization table");
return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
}
if (!fill_huffman_table(decoder, picture)) {
GST_ERROR("failed to fill in huffman table");
return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
}
scan_hdr_size = (buf[seg->offset] << 8) | buf[seg->offset + 1];
scan_data_size = seg->size - scan_hdr_size;
memset(&scan_hdr, 0, sizeof(scan_hdr));
if (!gst_jpeg_parse_scan_hdr(&scan_hdr, scan_header, scan_header_size, 0)) {
if (!gst_jpeg_parse_scan_hdr(&scan_hdr, buf + seg->offset, seg->size, 0)) {
GST_DEBUG("Jpeg parsed scan failed.");
return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
}
gst_slice = GST_VAAPI_SLICE_NEW(JPEGBaseline, decoder, scan_data, scan_data_size);
gst_vaapi_picture_add_slice(picture, gst_slice);
slice = GST_VAAPI_SLICE_NEW(JPEGBaseline, decoder,
buf + 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);
slice_param = gst_slice->param;
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 =
@ -512,128 +482,47 @@ decode_scan(
slice_param->num_mcus = ((priv->frame_hdr.width + total_h_samples*8 - 1)/(total_h_samples*8)) *
((priv->frame_hdr.height + total_v_samples*8 -1)/(total_v_samples*8));
}
if (picture->slices && picture->slices->len)
return GST_VAAPI_DECODER_STATUS_SUCCESS;
return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;
return GST_VAAPI_DECODER_STATUS_SUCCESS;
}
static GstVaapiDecoderStatus
decode_buffer(GstVaapiDecoderJpeg *decoder, const guchar *buf, guint buf_size)
decode_segment(GstVaapiDecoderJpeg *decoder, GstJpegMarkerSegment *seg,
const guchar *buf)
{
GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
GstVaapiDecoderStatus status = GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;
GstJpegMarkerSegment seg;
GstJpegScanSegment scan_seg;
guint ofs;
gboolean append_ecs;
GstVaapiDecoderStatus status;
memset(&scan_seg, 0, sizeof(scan_seg));
ofs = 0;
while (gst_jpeg_parse(&seg, buf, buf_size, ofs)) {
if (seg.size < 0) {
GST_DEBUG("buffer to short for parsing");
return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;
}
ofs = seg.offset + seg.size;
/* Decode scan, if complete */
if (seg.marker == GST_JPEG_MARKER_EOI && scan_seg.header_size > 0) {
scan_seg.data_size = seg.offset - scan_seg.data_offset;
scan_seg.is_valid = TRUE;
}
if (scan_seg.is_valid) {
status = decode_scan(
decoder,
buf + scan_seg.header_offset,
scan_seg.header_size,
buf + scan_seg.data_offset,
scan_seg.data_size
);
if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
break;
memset(&scan_seg, 0, sizeof(scan_seg));
}
append_ecs = TRUE;
switch (seg.marker) {
case GST_JPEG_MARKER_SOI:
priv->has_quant_table = FALSE;
priv->has_huf_table = FALSE;
priv->mcu_restart = 0;
status = GST_VAAPI_DECODER_STATUS_SUCCESS;
break;
case GST_JPEG_MARKER_EOI:
if (decode_current_picture(decoder)) {
/* Get out of the loop, trailing data is not needed */
status = GST_VAAPI_DECODER_STATUS_SUCCESS;
goto end;
}
status = GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
break;
case GST_JPEG_MARKER_DHT:
status = decode_huffman_table(decoder, buf + seg.offset, seg.size);
break;
case GST_JPEG_MARKER_DQT:
status = decode_quant_table(decoder, buf + seg.offset, seg.size);
break;
case GST_JPEG_MARKER_DRI:
status = decode_restart_interval(decoder, buf + seg.offset, seg.size);
break;
case GST_JPEG_MARKER_COM:
status = GST_VAAPI_DECODER_STATUS_SUCCESS;
break;
case GST_JPEG_MARKER_DAC:
GST_ERROR("unsupported arithmetic coding mode");
status = GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
break;
case GST_JPEG_MARKER_SOS:
scan_seg.header_offset = seg.offset;
scan_seg.header_size = seg.size;
scan_seg.data_offset = seg.offset + seg.size;
scan_seg.data_size = 0;
append_ecs = FALSE;
break;
default:
/* Restart marker */
if (seg.marker >= GST_JPEG_MARKER_RST_MIN &&
seg.marker <= GST_JPEG_MARKER_RST_MAX) {
append_ecs = FALSE;
break;
}
/* Frame header */
if (seg.marker >= GST_JPEG_MARKER_SOF_MIN &&
seg.marker <= GST_JPEG_MARKER_SOF_MAX) {
status = decode_picture(
decoder,
seg.marker,
buf + seg.offset, seg.size
);
break;
}
/* Application segments */
if (seg.marker >= GST_JPEG_MARKER_APP_MIN &&
seg.marker <= GST_JPEG_MARKER_APP_MAX) {
status = GST_VAAPI_DECODER_STATUS_SUCCESS;
break;
}
GST_WARNING("unsupported marker (0x%02x)", seg.marker);
status = GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
break;
}
/* Append entropy coded segments */
if (append_ecs)
scan_seg.data_size = seg.offset - scan_seg.data_offset;
if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
break;
// Decode segment
status = GST_VAAPI_DECODER_STATUS_SUCCESS;
switch (seg->marker) {
case GST_JPEG_MARKER_SOI:
priv->has_quant_table = FALSE;
priv->has_huf_table = FALSE;
priv->mcu_restart = 0;
break;
case GST_JPEG_MARKER_DAC:
GST_ERROR("unsupported arithmetic coding mode");
status = GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
break;
case GST_JPEG_MARKER_DHT:
status = decode_huffman_table(decoder, buf + seg->offset, seg->size);
break;
case GST_JPEG_MARKER_DQT:
status = decode_quant_table(decoder, buf + seg->offset, seg->size);
break;
case GST_JPEG_MARKER_DRI:
status = decode_restart_interval(decoder, buf + seg->offset, seg->size);
break;
case GST_JPEG_MARKER_SOS:
status = decode_scan(decoder, seg, buf);
break;
default:
// SOFn segments
if (seg->marker >= GST_JPEG_MARKER_SOF_MIN &&
seg->marker <= GST_JPEG_MARKER_SOF_MAX)
status = decode_picture(decoder, seg, buf);
break;
}
end:
return status;
}
@ -650,21 +539,11 @@ ensure_decoder(GstVaapiDecoderJpeg *decoder)
return GST_VAAPI_DECODER_STATUS_SUCCESS;
}
static gint
scan_for_marker_code(const guchar *buf, guint buf_size,
GstJpegMarkerCode marker)
static gboolean
is_scan_complete(GstJpegMarkerCode marker)
{
GstJpegMarkerSegment seg;
guint ofs = 0;
while (gst_jpeg_parse(&seg, buf, buf_size, ofs)) {
if (seg.size < 0)
return -1;
if (seg.marker == marker)
return seg.offset - 2;
ofs = seg.offset + seg.size;
}
return -1;
// Scan is assumed to be complete when the new segment is not RSTi
return marker < GST_JPEG_MARKER_RST_MIN || marker > GST_JPEG_MARKER_RST_MAX;
}
static GstVaapiDecoderStatus
@ -673,57 +552,113 @@ gst_vaapi_decoder_jpeg_parse(GstVaapiDecoder *base_decoder,
{
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;
GstJpegMarkerCode marker;
GstJpegMarkerSegment seg;
const guchar *buf;
guint buf_size, flags;
gint ofs, ofs1, ofs2;
gint ofs1, ofs2;
status = ensure_decoder(decoder);
if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
return status;
/* Expect at least 4 bytes, SOI .. EOI */
/* Expect at least 2 bytes for the marker */
buf_size = gst_adapter_available(adapter);
if (buf_size < 4)
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;
ofs = scan_for_marker_code(buf, buf_size, GST_JPEG_MARKER_SOI);
if (ofs < 0) {
gst_adapter_unmap(adapter);
return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;
}
ofs1 = ofs;
ofs1 = ps->input_offset1 - 2;
if (ofs1 < 0)
ofs1 = 0;
ofs2 = ps->input_offset2 - 2;
if (ofs2 < ofs1 + 2)
ofs2 = ofs1 + 2;
ofs = G_UNLIKELY(buf_size < ofs2 + 2) ? -1 :
scan_for_marker_code(&buf[ofs2], buf_size - ofs2, GST_JPEG_MARKER_EOI);
gst_adapter_unmap(adapter);
if (ofs < 0) {
// Assume the whole packet is present if end-of-stream
if (!at_eos) {
ps->input_offset2 = buf_size;
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;
}
ofs = buf_size - ofs2;
}
ofs2 += ofs;
ofs1 = seg.offset;
unit->size = buf_size;
marker = seg.marker;
if (priv->expect_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;
flags |= GST_VAAPI_DECODER_UNIT_FLAG_FRAME_START;
flags |= GST_VAAPI_DECODER_UNIT_FLAG_FRAME_END;
flags |= GST_VAAPI_DECODER_UNIT_FLAG_SLICE;
flags = 0;
switch (marker) {
case GST_JPEG_MARKER_SOI:
flags |= GST_VAAPI_DECODER_UNIT_FLAG_FRAME_START;
priv->expect_SOI = FALSE;
break;
case GST_JPEG_MARKER_EOI:
flags |= GST_VAAPI_DECODER_UNIT_FLAG_FRAME_END;
priv->expect_SOI = TRUE;
break;
case GST_JPEG_MARKER_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:
/* Application segments */
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;
}
@ -735,6 +670,7 @@ gst_vaapi_decoder_jpeg_decode(GstVaapiDecoder *base_decoder,
GstVaapiDecoderJpeg * const decoder =
GST_VAAPI_DECODER_JPEG_CAST(base_decoder);
GstVaapiDecoderStatus status;
GstJpegMarkerSegment seg;
GstBuffer * const buffer =
GST_VAAPI_DECODER_CODEC_FRAME(decoder)->input_buffer;
GstMapInfo map_info;
@ -748,13 +684,68 @@ gst_vaapi_decoder_jpeg_decode(GstVaapiDecoder *base_decoder,
return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
}
status = decode_buffer(decoder, map_info.data + unit->offset, unit->size);
seg.marker = unit_get_marker_code(unit);
seg.offset = unit->offset;
seg.size = unit->size;
status = decode_segment(decoder, &seg, map_info.data);
gst_buffer_unmap(buffer, &map_info);
if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
return status;
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;
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;
if (!fill_quantization_table(decoder, picture)) {
GST_ERROR("failed to fill in quantization table");
return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
}
if (!fill_huffman_table(decoder, picture)) {
GST_ERROR("failed to fill in huffman table");
return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
}
/* Update presentation time */
picture->pts = GST_VAAPI_DECODER_CODEC_FRAME(decoder)->pts;
return GST_VAAPI_DECODER_STATUS_SUCCESS;
}
static GstVaapiDecoderStatus
gst_vaapi_decoder_jpeg_end_frame(GstVaapiDecoder *base_decoder)
{
GstVaapiDecoderJpeg * const decoder =
GST_VAAPI_DECODER_JPEG_CAST(base_decoder);
return decode_current_picture(decoder);
}
static void
gst_vaapi_decoder_jpeg_class_init(GstVaapiDecoderJpegClass *klass)
{
@ -769,6 +760,8 @@ gst_vaapi_decoder_jpeg_class_init(GstVaapiDecoderJpegClass *klass)
decoder_class->destroy = gst_vaapi_decoder_jpeg_destroy;
decoder_class->parse = gst_vaapi_decoder_jpeg_parse;
decoder_class->decode = gst_vaapi_decoder_jpeg_decode;
decoder_class->start_frame = gst_vaapi_decoder_jpeg_start_frame;
decoder_class->end_frame = gst_vaapi_decoder_jpeg_end_frame;
}
static inline const GstVaapiDecoderClass *

View file

@ -168,6 +168,7 @@ struct _GstVaapiParserState {
GstVideoCodecFrame *current_frame;
GstAdapter *current_adapter;
GstAdapter *input_adapter;
gint input_offset1;
gint input_offset2;
GstAdapter *output_adapter;
GstVaapiDecoderUnit next_unit;