jpeg: make gst_jpeg_parse() support multiple scans.

gst_jpeg_parse() now gathers all scans available in the supplied
buffer. A scan comprises of the scan header and any entropy-coded
segments or restart marker following it. The size and offset to
the associated data (ECS + RST segments) are append to a new
GstJpegScanOffsetSize structure.

Signed-off-by: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
This commit is contained in:
Wind Yuan 2012-06-04 15:52:19 +08:00 committed by Gwenole Beauchesne
parent 53cbdcc1e6
commit 4c5cc7eff9
3 changed files with 115 additions and 105 deletions

View file

@ -438,6 +438,41 @@ gst_jpeg_get_default_quantization_table (GstJpegQuantTable *quant_tables, guint
sizeof(GstJpegQuantTable));
}
static gint32
jpeg_scan_to_end (const guint8 *start, guint32 size)
{
const guint8 *pos = start, *end = start + size;
for (; pos < end; ++pos) {
if (*pos != 0xFF)
continue;
while (*pos == 0xFF && pos + 1 < end)
++pos;
if (*pos == 0x00 || *pos == 0xFF ||
(*pos >= GST_JPEG_MARKER_RST_MIN && *pos <= GST_JPEG_MARKER_RST_MAX))
continue;
break;
}
if (pos >= end)
return size;
return pos - start - 1;
}
static GstJpegTypeOffsetSize *
gst_jpeg_segment_new (guint8 marker, guint offset, gint size)
{
GstJpegTypeOffsetSize *seg;
if (GST_JPEG_MARKER_SOS == marker)
seg = g_malloc0 (sizeof (GstJpegScanOffsetSize));
else
seg = g_malloc0 (sizeof (GstJpegTypeOffsetSize));
seg->type = marker;
seg->offset = offset;
seg->size = size;
return seg;
}
GList *
gst_jpeg_parse(const guint8 * data, gsize size, guint offset)
{
@ -446,6 +481,8 @@ gst_jpeg_parse(const guint8 * data, gsize size, guint offset)
guint16 header_length;
GList *segments = NULL;
GstJpegTypeOffsetSize *seg;
const guint8 *scan_start;
gint scan_size = 0;
size -= offset;
@ -470,16 +507,24 @@ gst_jpeg_parse(const guint8 * data, gsize size, guint offset)
gst_byte_reader_get_remaining (&bytes_reader) < header_length - 2)
goto failed;
seg = g_malloc (sizeof (GstJpegTypeOffsetSize));
seg->type = marker;
seg->offset = gst_byte_reader_get_pos(&bytes_reader) + offset;
seg->size = header_length - 2;
seg = gst_jpeg_segment_new (marker,
gst_byte_reader_get_pos(&bytes_reader) + offset,
header_length - 2);
segments = g_list_prepend(segments, seg);
gst_byte_reader_skip (&bytes_reader, header_length - 2);
/* parser should stop at first scan */
if (seg->type == GST_JPEG_MARKER_SOS)
break;
if (seg->type == GST_JPEG_MARKER_SOS) {
GstJpegScanOffsetSize * const scan_seg = (GstJpegScanOffsetSize *)seg;
scan_start = gst_byte_reader_peek_data_unchecked (&bytes_reader);
scan_size = jpeg_scan_to_end (scan_start,
gst_byte_reader_get_remaining (&bytes_reader));
if (scan_size <= 0)
break;
scan_seg->data_offset = gst_byte_reader_get_pos (&bytes_reader) + offset;
scan_seg->data_size = scan_size;
gst_byte_reader_skip (&bytes_reader, scan_size);
}
}
return g_list_reverse (segments);

View file

@ -59,6 +59,7 @@ typedef struct _GstJpegScanHdr GstJpegScanHdr;
typedef struct _GstJpegFrameComponent GstJpegFrameComponent;
typedef struct _GstJpegFrameHdr GstJpegFrameHdr;
typedef struct _GstJpegTypeOffsetSize GstJpegTypeOffsetSize;
typedef struct _GstJpegScanOffsetSize GstJpegScanOffsetSize;
/**
* GstJpegParserResult:
@ -241,6 +242,25 @@ struct _GstJpegTypeOffsetSize
gint size;
};
/**
* GstJpegScanOffsetSize:
* @header: The header info associated to the scan
* @data_offset: The offset to the first entropy-coded segment in bytes
* @data_size: The size in bytes of the scan data, including all ECS
* and RST segments, or -1 if the end was not found
*
* A structure that contains information on a scan. A scan comprises of the
* scan @header, and all entropy-coded segment (ECS) and restart marker (RST)
* associated to it. The header type MUST be set to @GST_JPEG_MARKER_SOS.
*/
struct _GstJpegScanOffsetSize
{
GstJpegTypeOffsetSize header;
guint data_offset;
gint data_size;
};
/**
* gst_jpeg_parse:
* @data: The data to parse

View file

@ -433,66 +433,19 @@ decode_restart_interval(
return GST_VAAPI_DECODER_STATUS_SUCCESS;
}
static gint32
scan_to_end(const guint8 *start, guint32 size)
{
const guint8 *pos = start, *end = start + size;
for (; pos < end; ++pos) {
if (*pos != 0xFF)
continue;
while (*pos == 0xFF && pos + 1 < end)
++pos;
if (*pos == 0x00 || *pos == 0xFF ||
(*pos >= GST_JPEG_MARKER_RST_MIN && *pos <= GST_JPEG_MARKER_RST_MAX))
continue;
break;
}
if (pos >= end)
return size;
return pos - start - 1;
}
static gboolean
scan_to_next_scan(guint8 *data, guint32 size,
guint8 **scan, guint32 *scan_header_size, guint32 *scan_left_size)
{
GList *seg_list = NULL;
gboolean ret = FALSE;
if (!data || !size)
return FALSE;
seg_list = gst_jpeg_parse(data, size, 0);
for (; seg_list; seg_list = seg_list->next) {
GstJpegTypeOffsetSize * const seg = seg_list->data;
if (seg->type != GST_JPEG_MARKER_SOS)
continue;
*scan = seg->offset + data;
*scan_header_size = seg->size;
*scan_left_size = size - seg->offset - seg->size;
ret = TRUE;
break;
}
g_list_free_full(seg_list, g_free);
return ret;
}
static GstVaapiDecoderStatus
decode_scan(
GstVaapiDecoderJpeg *decoder,
guchar *scan,
guchar *scan_header,
guint scan_header_size,
guint scan_left_size)
guchar *scan_data,
guint scan_data_size)
{
GstVaapiDecoderJpegPrivate * const priv = decoder->priv;
GstVaapiPicture *picture = priv->current_picture;
GstJpegParserResult result = GST_JPEG_PARSER_OK;
VASliceParameterBufferJPEG *slice_param;
GstVaapiSlice *gst_slice;
guint8 *mcu_start, *next_mark;
guint8 *buf_end = scan + scan_header_size + scan_left_size;
guint mcu_size;
guint total_h_samples, total_v_samples;
GstJpegScanHdr scan_hdr;
guint i;
@ -512,53 +465,40 @@ decode_scan(
return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
}
mcu_size = scan_left_size;
while (scan_left_size) {
memset(&scan_hdr, 0, sizeof(scan_hdr));
result = gst_jpeg_parse_scan_hdr(&scan_hdr, scan, scan_header_size, 0);
if (result != GST_JPEG_PARSER_OK) {
GST_DEBUG("Jpeg parsed scan failed.");
return get_status(result);
memset(&scan_hdr, 0, sizeof(scan_hdr));
result = gst_jpeg_parse_scan_hdr(&scan_hdr, scan_header, scan_header_size, 0);
if (result != GST_JPEG_PARSER_OK) {
GST_DEBUG("Jpeg parsed scan failed.");
return get_status(result);
}
gst_slice = GST_VAAPI_SLICE_NEW(JPEG, decoder, scan_data, scan_data_size);
gst_vaapi_picture_add_slice(picture, gst_slice);
slice_param = gst_slice->param;
slice_param->num_components = scan_hdr.num_components;
for (i = 0; i < scan_hdr.num_components; i++) {
slice_param->components[i].component_id = scan_hdr.components[i].component_selector;
slice_param->components[i].dc_selector = scan_hdr.components[i].dc_selector;
slice_param->components[i].ac_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_id == 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);
}
mcu_start = scan + scan_header_size;
mcu_size = scan_to_end(mcu_start, scan_left_size);
gst_slice = GST_VAAPI_SLICE_NEW(JPEG, decoder, mcu_start, mcu_size);
gst_vaapi_picture_add_slice(picture, gst_slice);
slice_param = gst_slice->param;
slice_param->num_components = scan_hdr.num_components;
for (i = 0; i < scan_hdr.num_components; i++) {
slice_param->components[i].component_id = scan_hdr.components[i].component_selector;
slice_param->components[i].dc_selector = scan_hdr.components[i].dc_selector;
slice_param->components[i].ac_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_id == 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));
}
next_mark = mcu_start + mcu_size;
scan_left_size = buf_end - next_mark;
if (scan_left_size < 4) /* Mark_code(2-bytes) + header_length(2-bytes)*/
break;
if (!scan_to_next_scan(next_mark, scan_left_size, &scan, &scan_header_size, &scan_left_size))
break;
} 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));
}
if (picture->slices && picture->slices->len)
@ -608,10 +548,15 @@ decode_buffer(GstVaapiDecoderJpeg *decoder, GstBuffer *buffer)
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, buf + seg->offset, seg->size, buf_size - seg->offset - seg->size);
case GST_JPEG_MARKER_SOS: {
GstJpegScanOffsetSize * const scan = (GstJpegScanOffsetSize *)seg;
status = decode_scan(
decoder, buf + scan->header.offset, scan->header.size,
buf + scan->data_offset, scan->data_size
);
scan_found = TRUE;
break;
}
default:
if (seg->type >= GST_JPEG_MARKER_SOF_MIN &&
seg->type <= GST_JPEG_MARKER_SOF_MAX)