mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-06-06 07:28:53 +00:00
jpegparse: improve parsing
Handle more app maker. Print app marker names to debug log. Remember last_marker when parsing to avoid reparsing from the very begin.
This commit is contained in:
parent
6f1ee59df6
commit
b9f8f5d281
2 changed files with 90 additions and 24 deletions
|
@ -76,6 +76,9 @@ G_BEGIN_DECLS
|
||||||
|
|
||||||
#define APP0 0xe0 /* Application marker */
|
#define APP0 0xe0 /* Application marker */
|
||||||
#define APP1 0xe1
|
#define APP1 0xe1
|
||||||
|
#define APP2 0xe2
|
||||||
|
#define APP13 0xed
|
||||||
|
#define APP14 0xee
|
||||||
#define APP15 0xef
|
#define APP15 0xef
|
||||||
|
|
||||||
#define JPG0 0xf0 /* Reserved ... */
|
#define JPG0 0xf0 /* Reserved ... */
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
#include <gst/base/gstbytereader.h>
|
#include <gst/base/gstbytereader.h>
|
||||||
|
|
||||||
#include "gstjpegparse.h"
|
#include "gstjpegparse.h"
|
||||||
|
@ -74,6 +75,7 @@ struct _GstJpegParsePrivate
|
||||||
GstPad *srcpad;
|
GstPad *srcpad;
|
||||||
|
|
||||||
GstAdapter *adapter;
|
GstAdapter *adapter;
|
||||||
|
guint last_offset;
|
||||||
|
|
||||||
/* negotiated state */
|
/* negotiated state */
|
||||||
gint caps_width, caps_height;
|
gint caps_width, caps_height;
|
||||||
|
@ -256,6 +258,7 @@ gst_jpeg_parse_skip_to_jpeg_header (GstJpegParse * parse)
|
||||||
available = gst_adapter_available (parse->priv->adapter);
|
available = gst_adapter_available (parse->priv->adapter);
|
||||||
if (available < 4)
|
if (available < 4)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
flush = gst_adapter_masked_scan_uint32 (parse->priv->adapter, 0xffffff00,
|
flush = gst_adapter_masked_scan_uint32 (parse->priv->adapter, 0xffffff00,
|
||||||
0xffd8ff00, 0, available);
|
0xffd8ff00, 0, available);
|
||||||
if (flush == -1) {
|
if (flush == -1) {
|
||||||
|
@ -300,24 +303,30 @@ gst_jpeg_parse_match_next_marker (const guint8 * data, guint size)
|
||||||
if (tag >= RST0 && tag <= EOI)
|
if (tag >= RST0 && tag <= EOI)
|
||||||
marker_len = 2;
|
marker_len = 2;
|
||||||
else if (G_UNLIKELY (size < 4))
|
else if (G_UNLIKELY (size < 4))
|
||||||
return -1;
|
goto need_more_data;
|
||||||
else
|
else
|
||||||
marker_len = GST_READ_UINT16_BE (data + 2) + 2;
|
marker_len = GST_READ_UINT16_BE (data + 2) + 2;
|
||||||
|
|
||||||
GST_LOG ("Have marker %x with length %u", data[1], marker_len);
|
/* we log this in gst_jpeg_parse_find_end_marker() already
|
||||||
|
GST_LOG ("Have marker %x with length %u", data[1], marker_len);
|
||||||
|
*/
|
||||||
|
|
||||||
/* Need marker_len for this marker, plus two for the next marker. */
|
/* Need marker_len for this marker, plus two for the next marker. */
|
||||||
if (G_UNLIKELY (marker_len + 2 >= size))
|
if (G_UNLIKELY (marker_len + 2 >= size))
|
||||||
return -1;
|
goto need_more_data;
|
||||||
|
|
||||||
if (G_UNLIKELY (gst_jpeg_parse_parse_tag_has_entropy_segment (tag))) {
|
if (G_UNLIKELY (gst_jpeg_parse_parse_tag_has_entropy_segment (tag))) {
|
||||||
while (!(data[marker_len] == 0xff && data[marker_len + 1] != 0x00)) {
|
while (!(data[marker_len] == 0xff && data[marker_len + 1] != 0x00)) {
|
||||||
if (G_UNLIKELY (marker_len + 2 >= size))
|
|
||||||
return -1;
|
|
||||||
++marker_len;
|
++marker_len;
|
||||||
|
if (G_UNLIKELY (marker_len > size))
|
||||||
|
goto need_more_data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return marker_len;
|
return marker_len;
|
||||||
|
|
||||||
|
need_more_data:
|
||||||
|
GST_LOG ("need more data");
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -334,7 +343,7 @@ static guint
|
||||||
gst_jpeg_parse_find_end_marker (GstJpegParse * parse, const guint8 * data,
|
gst_jpeg_parse_find_end_marker (GstJpegParse * parse, const guint8 * data,
|
||||||
guint size)
|
guint size)
|
||||||
{
|
{
|
||||||
guint offset = 0;
|
guint offset = parse->priv->last_offset;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
guint marker_len;
|
guint marker_len;
|
||||||
|
@ -343,6 +352,7 @@ gst_jpeg_parse_find_end_marker (GstJpegParse * parse, const guint8 * data,
|
||||||
if (offset + 1 >= size)
|
if (offset + 1 >= size)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
/* all jpeg marker start with 0xff */
|
||||||
if (data[offset] != 0xff)
|
if (data[offset] != 0xff)
|
||||||
return -2;
|
return -2;
|
||||||
|
|
||||||
|
@ -355,7 +365,8 @@ gst_jpeg_parse_find_end_marker (GstJpegParse * parse, const guint8 * data,
|
||||||
/* Check for EOI */
|
/* Check for EOI */
|
||||||
if (G_UNLIKELY (tag == EOI)) {
|
if (G_UNLIKELY (tag == EOI)) {
|
||||||
GST_DEBUG_OBJECT (parse, "EOI at %u", offset);
|
GST_DEBUG_OBJECT (parse, "EOI at %u", offset);
|
||||||
return offset + 2;
|
parse->priv->last_offset = offset;
|
||||||
|
return offset;
|
||||||
}
|
}
|
||||||
/* Skip over this marker. */
|
/* Skip over this marker. */
|
||||||
marker_len = gst_jpeg_parse_match_next_marker (data + offset,
|
marker_len = gst_jpeg_parse_match_next_marker (data + offset,
|
||||||
|
@ -365,6 +376,8 @@ gst_jpeg_parse_find_end_marker (GstJpegParse * parse, const guint8 * data,
|
||||||
} else {
|
} else {
|
||||||
GST_LOG_OBJECT (parse, "At offset %u: marker %02x, length %u", offset,
|
GST_LOG_OBJECT (parse, "At offset %u: marker %02x, length %u", offset,
|
||||||
tag, marker_len);
|
tag, marker_len);
|
||||||
|
/* remember last found marker, so that we don't rescan from begin */
|
||||||
|
parse->priv->last_offset = offset;
|
||||||
offset += marker_len;
|
offset += marker_len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -375,7 +388,7 @@ static guint
|
||||||
gst_jpeg_parse_get_image_length (GstJpegParse * parse)
|
gst_jpeg_parse_get_image_length (GstJpegParse * parse)
|
||||||
{
|
{
|
||||||
const guint8 *data;
|
const guint8 *data;
|
||||||
guint size, offset, start = 2;
|
guint size, offset;
|
||||||
|
|
||||||
size = gst_adapter_available (parse->priv->adapter);
|
size = gst_adapter_available (parse->priv->adapter);
|
||||||
if (size < 4) {
|
if (size < 4) {
|
||||||
|
@ -386,35 +399,34 @@ gst_jpeg_parse_get_image_length (GstJpegParse * parse)
|
||||||
|
|
||||||
g_return_val_if_fail (data[0] == 0xff && data[1] == SOI, 0);
|
g_return_val_if_fail (data[0] == 0xff && data[1] == SOI, 0);
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (parse, "Parsing jpeg image data (%u bytes)", size);
|
offset = gst_jpeg_parse_find_end_marker (parse, data, size);
|
||||||
|
|
||||||
/* skip start marker */
|
|
||||||
offset = gst_jpeg_parse_find_end_marker (parse, data + 2, size - 2);
|
|
||||||
|
|
||||||
if (offset == -1) {
|
if (offset == -1) {
|
||||||
GST_DEBUG_OBJECT (parse, "Insufficient data.");
|
GST_LOG_OBJECT (parse, "Insufficient data.");
|
||||||
/* FIXME: remember size, so that we don't rescan from begin */
|
|
||||||
return 0;
|
return 0;
|
||||||
} else if (G_UNLIKELY (offset == -2)) {
|
} else if (G_UNLIKELY (offset == -2)) {
|
||||||
|
guint start = parse->priv->last_offset;
|
||||||
GST_DEBUG_OBJECT (parse, "Lost sync, resyncing.");
|
GST_DEBUG_OBJECT (parse, "Lost sync, resyncing.");
|
||||||
/* FIXME does this make sense at all? This can only happen for broken
|
/* FIXME does this make sense at all? This can only happen for broken
|
||||||
* images, and the most likely breakage is that it's truncated. In that
|
* images, and the most likely breakage is that it's truncated. In that
|
||||||
* case, however, we should be looking for a new start marker... */
|
* case, however, we should be looking for a new start marker... */
|
||||||
while (offset == -2 || offset == -1) {
|
while (offset == -2 || offset == -1) {
|
||||||
start++;
|
start++;
|
||||||
|
/* scan for 0xff */
|
||||||
while (start + 1 < size && data[start] != 0xff)
|
while (start + 1 < size && data[start] != 0xff)
|
||||||
start++;
|
start++;
|
||||||
if (G_UNLIKELY (start + 1 >= size)) {
|
if (G_UNLIKELY (start + 1 >= size)) {
|
||||||
GST_DEBUG_OBJECT (parse, "Insufficient data while resyncing.");
|
GST_DEBUG_OBJECT (parse, "Insufficient data while resyncing.");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
GST_LOG_OBJECT (parse, "Resyncing from offset %u.", start);
|
GST_LOG_OBJECT (parse, "Resyncing from offset %u (size %u).",
|
||||||
offset = gst_jpeg_parse_find_end_marker (parse, data + start, size -
|
start, size);
|
||||||
start);
|
parse->priv->last_offset = start;
|
||||||
|
offset = gst_jpeg_parse_find_end_marker (parse, data, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return start + offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -547,7 +559,21 @@ gst_jpeg_parse_read_header (GstJpegParse * parse, GstBuffer * buffer)
|
||||||
|
|
||||||
case APP0:
|
case APP0:
|
||||||
case APP1:
|
case APP1:
|
||||||
case APP15:
|
case APP2:
|
||||||
|
case APP13:
|
||||||
|
case APP14:
|
||||||
|
case APP15:{
|
||||||
|
const gchar *id_str;
|
||||||
|
if (!gst_byte_reader_get_uint16_be (&reader, &size))
|
||||||
|
goto error;
|
||||||
|
if (!gst_byte_reader_get_string_utf8 (&reader, &id_str))
|
||||||
|
goto error;
|
||||||
|
if (!gst_byte_reader_skip (&reader, size - 3 - strlen (id_str)))
|
||||||
|
goto error;
|
||||||
|
GST_LOG_OBJECT (parse, "unhandled marker %x: '%s' skiping %u bytes",
|
||||||
|
marker, id_str, size - 2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case DHT:
|
case DHT:
|
||||||
case DQT:
|
case DQT:
|
||||||
/* Ignore these codes */
|
/* Ignore these codes */
|
||||||
|
@ -571,10 +597,38 @@ gst_jpeg_parse_read_header (GstJpegParse * parse, GstBuffer * buffer)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
GST_WARNING_OBJECT (parse, "unhandled marker %x, leaving", marker);
|
if (marker == JPG || (marker >= JPG0 && marker <= JPG13)) {
|
||||||
/* Not SOF or SOI. Must not be a JPEG file (or file pointer
|
/* we'd like to remove them from the buffer */
|
||||||
* is placed wrong). In either case, it's an error. */
|
#if 1
|
||||||
return FALSE;
|
guint pos = gst_byte_reader_get_pos (&reader);
|
||||||
|
guint8 *data = GST_BUFFER_DATA (buffer);
|
||||||
|
|
||||||
|
if (!gst_byte_reader_peek_uint16_be (&reader, &size))
|
||||||
|
goto error;
|
||||||
|
if (gst_byte_reader_get_remaining (&reader) < size)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (parse, "unhandled marker %x removing %u bytes",
|
||||||
|
marker, size);
|
||||||
|
|
||||||
|
memmove (&data[pos], &data[pos + size],
|
||||||
|
GST_BUFFER_SIZE (buffer) - (pos + size));
|
||||||
|
GST_BUFFER_SIZE (buffer) -= size;
|
||||||
|
reader.size -= size;
|
||||||
|
#else
|
||||||
|
if (!gst_byte_reader_get_uint16_be (&reader, &size))
|
||||||
|
goto error;
|
||||||
|
if (!gst_byte_reader_skip (&reader, size - 2))
|
||||||
|
goto error;
|
||||||
|
GST_LOG_OBJECT (parse, "unhandled marker %x skiping %u bytes", marker,
|
||||||
|
size - 2);
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
GST_WARNING_OBJECT (parse, "unhandled marker %x, leaving", marker);
|
||||||
|
/* Not SOF or SOI. Must not be a JPEG file (or file pointer
|
||||||
|
* is placed wrong). In either case, it's an error. */
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!gst_byte_reader_peek_uint8 (&reader, &marker))
|
if (!gst_byte_reader_peek_uint8 (&reader, &marker))
|
||||||
|
@ -584,7 +638,9 @@ gst_jpeg_parse_read_header (GstJpegParse * parse, GstBuffer * buffer)
|
||||||
return foundSOF;
|
return foundSOF;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
GST_WARNING_OBJECT (parse, "Error parsing image header");
|
GST_WARNING_OBJECT (parse,
|
||||||
|
"Error parsing image header (need more that %u bytes available)",
|
||||||
|
gst_byte_reader_get_remaining (&reader));
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -642,6 +698,9 @@ gst_jpeg_parse_push_buffer (GstJpegParse * parse, guint len)
|
||||||
GstFlowReturn ret = GST_FLOW_OK;
|
GstFlowReturn ret = GST_FLOW_OK;
|
||||||
gboolean header_ok;
|
gboolean header_ok;
|
||||||
|
|
||||||
|
/* reset the offset (only when we flushed) */
|
||||||
|
parse->priv->last_offset = 2;
|
||||||
|
|
||||||
outbuf = gst_adapter_take_buffer (parse->priv->adapter, len);
|
outbuf = gst_adapter_take_buffer (parse->priv->adapter, len);
|
||||||
if (outbuf == NULL) {
|
if (outbuf == NULL) {
|
||||||
GST_ELEMENT_ERROR (parse, STREAM, DECODE,
|
GST_ELEMENT_ERROR (parse, STREAM, DECODE,
|
||||||
|
@ -716,10 +775,12 @@ gst_jpeg_parse_chain (GstPad * pad, GstBuffer * buf)
|
||||||
|
|
||||||
parse->priv->duration = duration;
|
parse->priv->duration = duration;
|
||||||
|
|
||||||
|
/* check if we already have a EOI */
|
||||||
len = gst_jpeg_parse_get_image_length (parse);
|
len = gst_jpeg_parse_get_image_length (parse);
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
return GST_FLOW_OK;
|
return GST_FLOW_OK;
|
||||||
|
|
||||||
|
/* now we have enough in the adapter to process a full jpeg image */
|
||||||
ret = gst_jpeg_parse_push_buffer (parse, len);
|
ret = gst_jpeg_parse_push_buffer (parse, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -786,6 +847,8 @@ gst_jpeg_parse_change_state (GstElement * element, GstStateChange transition)
|
||||||
parse->priv->new_segment = FALSE;
|
parse->priv->new_segment = FALSE;
|
||||||
|
|
||||||
parse->priv->next_ts = GST_CLOCK_TIME_NONE;
|
parse->priv->next_ts = GST_CLOCK_TIME_NONE;
|
||||||
|
|
||||||
|
parse->priv->last_offset = 2;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue