mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-10 03:19:40 +00:00
f52dfe5797
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.
794 lines
26 KiB
C
794 lines
26 KiB
C
/*
|
|
* gstvaapidecoder_jpeg.c - JPEG decoder
|
|
*
|
|
* Copyright (C) 2011-2013 Intel Corporation
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public License
|
|
* as published by the Free Software Foundation; either version 2.1
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free
|
|
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/**
|
|
* SECTION:gstvaapidecoder_jpeg
|
|
* @short_description: JPEG decoder
|
|
*/
|
|
|
|
#include "sysdeps.h"
|
|
#include <string.h>
|
|
#include <gst/codecparsers/gstjpegparser.h>
|
|
#include "gstvaapicompat.h"
|
|
#include "gstvaapidecoder_jpeg.h"
|
|
#include "gstvaapidecoder_objects.h"
|
|
#include "gstvaapidecoder_priv.h"
|
|
#include "gstvaapidisplay_priv.h"
|
|
#include "gstvaapiobject_priv.h"
|
|
|
|
#ifdef HAVE_VA_VA_DEC_JPEG_H
|
|
# include <va/va_dec_jpeg.h>
|
|
#endif
|
|
|
|
#define DEBUG 1
|
|
#include "gstvaapidebug.h"
|
|
|
|
#define GST_VAAPI_DECODER_JPEG_CAST(decoder) \
|
|
((GstVaapiDecoderJpeg *)(decoder))
|
|
|
|
typedef struct _GstVaapiDecoderJpegPrivate GstVaapiDecoderJpegPrivate;
|
|
typedef struct _GstVaapiDecoderJpegClass GstVaapiDecoderJpegClass;
|
|
|
|
struct _GstVaapiDecoderJpegPrivate {
|
|
GstVaapiProfile profile;
|
|
guint width;
|
|
guint height;
|
|
GstVaapiPicture *current_picture;
|
|
GstJpegFrameHdr frame_hdr;
|
|
GstJpegHuffmanTables huf_tables;
|
|
GstJpegQuantTables quant_tables;
|
|
gboolean has_huf_table;
|
|
gboolean has_quant_table;
|
|
guint mcu_restart;
|
|
guint is_opened : 1;
|
|
guint profile_changed : 1;
|
|
guint expect_SOI : 1;
|
|
};
|
|
|
|
/**
|
|
* GstVaapiDecoderJpeg:
|
|
*
|
|
* A decoder based on Jpeg.
|
|
*/
|
|
struct _GstVaapiDecoderJpeg {
|
|
/*< private >*/
|
|
GstVaapiDecoder parent_instance;
|
|
GstVaapiDecoderJpegPrivate priv;
|
|
};
|
|
|
|
/**
|
|
* GstVaapiDecoderJpegClass:
|
|
*
|
|
* A decoder class based on Jpeg.
|
|
*/
|
|
struct _GstVaapiDecoderJpegClass {
|
|
/*< private >*/
|
|
GstVaapiDecoderClass parent_class;
|
|
};
|
|
|
|
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)
|
|
{
|
|
GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
|
|
|
|
gst_vaapi_picture_replace(&priv->current_picture, NULL);
|
|
|
|
/* Reset all */
|
|
priv->profile = GST_VAAPI_PROFILE_JPEG_BASELINE;
|
|
priv->width = 0;
|
|
priv->height = 0;
|
|
priv->is_opened = FALSE;
|
|
priv->profile_changed = TRUE;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static void
|
|
gst_vaapi_decoder_jpeg_destroy(GstVaapiDecoder *base_decoder)
|
|
{
|
|
GstVaapiDecoderJpeg * const decoder =
|
|
GST_VAAPI_DECODER_JPEG_CAST(base_decoder);
|
|
|
|
gst_vaapi_decoder_jpeg_close(decoder);
|
|
}
|
|
|
|
static gboolean
|
|
gst_vaapi_decoder_jpeg_create(GstVaapiDecoder *base_decoder)
|
|
{
|
|
GstVaapiDecoderJpeg * const decoder =
|
|
GST_VAAPI_DECODER_JPEG_CAST(base_decoder);
|
|
GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
|
|
|
|
priv->profile = GST_VAAPI_PROFILE_JPEG_BASELINE;
|
|
priv->profile_changed = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
static GstVaapiDecoderStatus
|
|
ensure_context(GstVaapiDecoderJpeg *decoder)
|
|
{
|
|
GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
|
|
GstVaapiProfile profiles[2];
|
|
GstVaapiEntrypoint entrypoint = GST_VAAPI_ENTRYPOINT_VLD;
|
|
guint i, n_profiles = 0;
|
|
gboolean reset_context = FALSE;
|
|
|
|
if (priv->profile_changed) {
|
|
GST_DEBUG("profile changed");
|
|
priv->profile_changed = FALSE;
|
|
reset_context = TRUE;
|
|
|
|
profiles[n_profiles++] = priv->profile;
|
|
//if (priv->profile == GST_VAAPI_PROFILE_JPEG_EXTENDED)
|
|
// profiles[n_profiles++] = GST_VAAPI_PROFILE_JPEG_BASELINE;
|
|
|
|
for (i = 0; i < n_profiles; i++) {
|
|
if (gst_vaapi_display_has_decoder(GST_VAAPI_DECODER_DISPLAY(decoder),
|
|
profiles[i], entrypoint))
|
|
break;
|
|
}
|
|
if (i == n_profiles)
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
|
|
priv->profile = profiles[i];
|
|
}
|
|
|
|
if (reset_context) {
|
|
GstVaapiContextInfo info;
|
|
|
|
info.profile = priv->profile;
|
|
info.entrypoint = entrypoint;
|
|
info.width = priv->width;
|
|
info.height = priv->height;
|
|
info.ref_frames = 2;
|
|
reset_context = gst_vaapi_decoder_ensure_context(
|
|
GST_VAAPI_DECODER(decoder),
|
|
&info
|
|
);
|
|
if (!reset_context)
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
|
|
}
|
|
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 (!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
|
|
fill_picture(
|
|
GstVaapiDecoderJpeg *decoder,
|
|
GstVaapiPicture *picture,
|
|
GstJpegFrameHdr *jpeg_frame_hdr
|
|
)
|
|
{
|
|
VAPictureParameterBufferJPEGBaseline *pic_param = picture->param;
|
|
guint i;
|
|
|
|
g_assert(pic_param);
|
|
|
|
memset(pic_param, 0, sizeof(VAPictureParameterBufferJPEGBaseline));
|
|
pic_param->picture_width = jpeg_frame_hdr->width;
|
|
pic_param->picture_height = jpeg_frame_hdr->height;
|
|
|
|
pic_param->num_components = jpeg_frame_hdr->num_components;
|
|
if (jpeg_frame_hdr->num_components > 4)
|
|
return FALSE;
|
|
for (i = 0; i < pic_param->num_components; i++) {
|
|
pic_param->components[i].component_id =
|
|
jpeg_frame_hdr->components[i].identifier;
|
|
pic_param->components[i].h_sampling_factor =
|
|
jpeg_frame_hdr->components[i].horizontal_factor;
|
|
pic_param->components[i].v_sampling_factor =
|
|
jpeg_frame_hdr->components[i].vertical_factor;
|
|
pic_param->components[i].quantiser_table_selector =
|
|
jpeg_frame_hdr->components[i].quant_table_selector;
|
|
}
|
|
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
|
|
fill_huffman_table(
|
|
GstVaapiDecoderJpeg *decoder,
|
|
GstVaapiPicture *picture
|
|
)
|
|
{
|
|
GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
|
|
GstJpegHuffmanTables * const huf_tables = &priv->huf_tables;
|
|
VAHuffmanTableBufferJPEGBaseline *huffman_table;
|
|
guint i, num_tables;
|
|
|
|
if (!priv->has_huf_table)
|
|
gst_jpeg_get_default_huffman_tables(&priv->huf_tables);
|
|
|
|
picture->huf_table = GST_VAAPI_HUFFMAN_TABLE_NEW(JPEGBaseline, decoder);
|
|
g_assert(picture->huf_table);
|
|
huffman_table = picture->huf_table->param;
|
|
|
|
num_tables = MIN(G_N_ELEMENTS(huffman_table->huffman_table),
|
|
GST_JPEG_MAX_SCAN_COMPONENTS);
|
|
|
|
for (i = 0; i < num_tables; i++) {
|
|
huffman_table->load_huffman_table[i] =
|
|
huf_tables->dc_tables[i].valid && huf_tables->ac_tables[i].valid;
|
|
if (!huffman_table->load_huffman_table[i])
|
|
continue;
|
|
|
|
memcpy(huffman_table->huffman_table[i].num_dc_codes,
|
|
huf_tables->dc_tables[i].huf_bits,
|
|
sizeof(huffman_table->huffman_table[i].num_dc_codes));
|
|
memcpy(huffman_table->huffman_table[i].dc_values,
|
|
huf_tables->dc_tables[i].huf_values,
|
|
sizeof(huffman_table->huffman_table[i].dc_values));
|
|
memcpy(huffman_table->huffman_table[i].num_ac_codes,
|
|
huf_tables->ac_tables[i].huf_bits,
|
|
sizeof(huffman_table->huffman_table[i].num_ac_codes));
|
|
memcpy(huffman_table->huffman_table[i].ac_values,
|
|
huf_tables->ac_tables[i].huf_values,
|
|
sizeof(huffman_table->huffman_table[i].ac_values));
|
|
memset(huffman_table->huffman_table[i].pad,
|
|
0,
|
|
sizeof(huffman_table->huffman_table[i].pad));
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static guint
|
|
get_max_horizontal_samples(GstJpegFrameHdr *frame_hdr)
|
|
{
|
|
guint i, max_factor = 0;
|
|
|
|
for (i = 0; i < frame_hdr->num_components; i++) {
|
|
if (frame_hdr->components[i].horizontal_factor > max_factor)
|
|
max_factor = frame_hdr->components[i].horizontal_factor;
|
|
}
|
|
return max_factor;
|
|
}
|
|
|
|
static guint
|
|
get_max_vertical_samples(GstJpegFrameHdr *frame_hdr)
|
|
{
|
|
guint i, max_factor = 0;
|
|
|
|
for (i = 0; i < frame_hdr->num_components; i++) {
|
|
if (frame_hdr->components[i].vertical_factor > max_factor)
|
|
max_factor = frame_hdr->components[i].vertical_factor;
|
|
}
|
|
return max_factor;
|
|
}
|
|
|
|
static GstVaapiDecoderStatus
|
|
decode_picture(GstVaapiDecoderJpeg *decoder, GstJpegMarkerSegment *seg,
|
|
const guchar *buf)
|
|
{
|
|
GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
|
|
GstJpegFrameHdr * const frame_hdr = &priv->frame_hdr;
|
|
|
|
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_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;
|
|
return GST_VAAPI_DECODER_STATUS_SUCCESS;
|
|
}
|
|
|
|
static GstVaapiDecoderStatus
|
|
decode_huffman_table(
|
|
GstVaapiDecoderJpeg *decoder,
|
|
const guchar *buf,
|
|
guint buf_size
|
|
)
|
|
{
|
|
GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
|
|
|
|
if (!gst_jpeg_parse_huffman_table(&priv->huf_tables, buf, buf_size, 0)) {
|
|
GST_DEBUG("failed to parse Huffman table");
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
|
|
}
|
|
priv->has_huf_table = TRUE;
|
|
return GST_VAAPI_DECODER_STATUS_SUCCESS;
|
|
}
|
|
|
|
static GstVaapiDecoderStatus
|
|
decode_quant_table(
|
|
GstVaapiDecoderJpeg *decoder,
|
|
const guchar *buf,
|
|
guint buf_size
|
|
)
|
|
{
|
|
GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
|
|
|
|
if (!gst_jpeg_parse_quant_table(&priv->quant_tables, buf, buf_size, 0)) {
|
|
GST_DEBUG("failed to parse quantization table");
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
|
|
}
|
|
priv->has_quant_table = TRUE;
|
|
return GST_VAAPI_DECODER_STATUS_SUCCESS;
|
|
}
|
|
|
|
static GstVaapiDecoderStatus
|
|
decode_restart_interval(
|
|
GstVaapiDecoderJpeg *decoder,
|
|
const guchar *buf,
|
|
guint buf_size
|
|
)
|
|
{
|
|
GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
|
|
|
|
if (!gst_jpeg_parse_restart_interval(&priv->mcu_restart, buf, buf_size, 0)) {
|
|
GST_DEBUG("failed to parse restart interval");
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
|
|
}
|
|
return GST_VAAPI_DECODER_STATUS_SUCCESS;
|
|
}
|
|
|
|
static GstVaapiDecoderStatus
|
|
decode_scan(GstVaapiDecoderJpeg *decoder, GstJpegMarkerSegment *seg,
|
|
const guchar *buf)
|
|
{
|
|
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, total_h_samples, total_v_samples;
|
|
|
|
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, buf + seg->offset, seg->size, 0)) {
|
|
GST_DEBUG("Jpeg parsed scan failed.");
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
|
|
}
|
|
|
|
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 = 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;
|
|
if (scan_hdr.num_components == 1) { /*non-interleaved*/
|
|
slice_param->slice_horizontal_position = 0;
|
|
slice_param->slice_vertical_position = 0;
|
|
/* Y mcu numbers*/
|
|
if (slice_param->components[0].component_selector == priv->frame_hdr.components[0].identifier) {
|
|
slice_param->num_mcus = (priv->frame_hdr.width/8)*(priv->frame_hdr.height/8);
|
|
} else { /*Cr, Cb mcu numbers*/
|
|
slice_param->num_mcus = (priv->frame_hdr.width/16)*(priv->frame_hdr.height/16);
|
|
}
|
|
} else { /* interleaved */
|
|
slice_param->slice_horizontal_position = 0;
|
|
slice_param->slice_vertical_position = 0;
|
|
total_v_samples = get_max_vertical_samples(&priv->frame_hdr);
|
|
total_h_samples = get_max_horizontal_samples(&priv->frame_hdr);
|
|
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));
|
|
}
|
|
return GST_VAAPI_DECODER_STATUS_SUCCESS;
|
|
}
|
|
|
|
static GstVaapiDecoderStatus
|
|
decode_segment(GstVaapiDecoderJpeg *decoder, GstJpegMarkerSegment *seg,
|
|
const guchar *buf)
|
|
{
|
|
GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
|
|
GstVaapiDecoderStatus status;
|
|
|
|
// 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;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
static GstVaapiDecoderStatus
|
|
ensure_decoder(GstVaapiDecoderJpeg *decoder)
|
|
{
|
|
GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
|
|
|
|
if (!priv->is_opened) {
|
|
priv->is_opened = gst_vaapi_decoder_jpeg_open(decoder);
|
|
if (!priv->is_opened)
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_CODEC;
|
|
}
|
|
return GST_VAAPI_DECODER_STATUS_SUCCESS;
|
|
}
|
|
|
|
static gboolean
|
|
is_scan_complete(GstJpegMarkerCode marker)
|
|
{
|
|
// 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
|
|
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;
|
|
GstJpegMarkerCode marker;
|
|
GstJpegMarkerSegment 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 (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;
|
|
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;
|
|
}
|
|
|
|
static GstVaapiDecoderStatus
|
|
gst_vaapi_decoder_jpeg_decode(GstVaapiDecoder *base_decoder,
|
|
GstVaapiDecoderUnit *unit)
|
|
{
|
|
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;
|
|
|
|
status = ensure_decoder(decoder);
|
|
if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
|
|
return status;
|
|
|
|
if (!gst_buffer_map(buffer, &map_info, GST_MAP_READ)) {
|
|
GST_ERROR("failed to map buffer");
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
|
|
}
|
|
|
|
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)
|
|
{
|
|
GstVaapiMiniObjectClass * const object_class =
|
|
GST_VAAPI_MINI_OBJECT_CLASS(klass);
|
|
GstVaapiDecoderClass * const decoder_class = GST_VAAPI_DECODER_CLASS(klass);
|
|
|
|
object_class->size = sizeof(GstVaapiDecoderJpeg);
|
|
object_class->finalize = (GDestroyNotify)gst_vaapi_decoder_finalize;
|
|
|
|
decoder_class->create = gst_vaapi_decoder_jpeg_create;
|
|
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 *
|
|
gst_vaapi_decoder_jpeg_class(void)
|
|
{
|
|
static GstVaapiDecoderJpegClass g_class;
|
|
static gsize g_class_init = FALSE;
|
|
|
|
if (g_once_init_enter(&g_class_init)) {
|
|
gst_vaapi_decoder_jpeg_class_init(&g_class);
|
|
g_once_init_leave(&g_class_init, TRUE);
|
|
}
|
|
return GST_VAAPI_DECODER_CLASS(&g_class);
|
|
}
|
|
|
|
/**
|
|
* gst_vaapi_decoder_jpeg_new:
|
|
* @display: a #GstVaapiDisplay
|
|
* @caps: a #GstCaps holding codec information
|
|
*
|
|
* Creates a new #GstVaapiDecoder for JPEG decoding. The @caps can
|
|
* hold extra information like codec-data and pictured coded size.
|
|
*
|
|
* Return value: the newly allocated #GstVaapiDecoder object
|
|
*/
|
|
GstVaapiDecoder *
|
|
gst_vaapi_decoder_jpeg_new(GstVaapiDisplay *display, GstCaps *caps)
|
|
{
|
|
return gst_vaapi_decoder_new(gst_vaapi_decoder_jpeg_class(), display, caps);
|
|
}
|