jpegparse: reset parse state when the SOI is not the first marker
authorHe Junyan <junyan.he@intel.com>
Sun, 12 Feb 2023 14:37:01 +0000 (22:37 +0800)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Tue, 7 Mar 2023 23:05:32 +0000 (23:05 +0000)
There may be garbage or some bits before a SOI comes in some problematic
mjpeg streams. For example, some network error may cause the EOI marker
of the previous frame lost, and when the new frame's SOI comes, we still
use the state of the last frame, which will generate errors.

For this kind of frames without EOI, if that frame already has some data
(the SOS segment is detected), we still push it as a frame with CORRUPTED
flag set. But if not, we just discard all the data before the new SOI.

Co-Authored-By: Víctor Jáquez <vjaquez@igalia.com>
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4130>

subprojects/gst-plugins-bad/gst/jpegformat/gstjpegparse.c

index 81c31d7..f30c8aa 100644 (file)
@@ -653,10 +653,14 @@ gst_jpeg_parse_set_new_caps (GstJpegParse * parse)
 }
 
 static GstFlowReturn
-gst_jpeg_parse_push_frame (GstJpegParse * parse, GstBaseParseFrame * frame,
+gst_jpeg_parse_finish_frame (GstJpegParse * parse, GstBaseParseFrame * frame,
     gint size)
 {
   GstBaseParse *bparse = GST_BASE_PARSE (parse);
+  GstFlowReturn ret;
+
+  if (parse->tags)
+    gst_base_parse_merge_tags (bparse, parse->tags, GST_TAG_MERGE_REPLACE);
 
   if (!gst_jpeg_parse_set_new_caps (parse))
     return GST_FLOW_ERROR;
@@ -667,7 +671,11 @@ gst_jpeg_parse_push_frame (GstJpegParse * parse, GstBaseParseFrame * frame,
     GST_WARNING_OBJECT (parse, "Potentially invalid picture");
   }
 
-  return gst_base_parse_finish_frame (bparse, frame, size);
+  ret = gst_base_parse_finish_frame (bparse, frame, size);
+
+  gst_jpeg_parse_reset (parse);
+
+  return ret;
 }
 
 static GstFlowReturn
@@ -722,32 +730,41 @@ gst_jpeg_parse_handle_frame (GstBaseParse * bparse, GstBaseParseFrame * frame,
 
     switch (marker) {
       case GST_JPEG_MARKER_SOI:
-        parse->state |= GST_JPEG_PARSER_STATE_GOT_SOI;
-        /* unset tags */
-        gst_base_parse_merge_tags (bparse, NULL, GST_TAG_MERGE_UNDEFINED);
-        /* remove all previous bytes */
+        /* This means that new SOI comes without an previous EOI. */
         if (offset > 2) {
+          /* If already some data segment parsed, push it as a frame. */
+          if (valid_state (parse->state, GST_JPEG_PARSER_STATE_GOT_SOS)) {
+            gst_buffer_unmap (frame->buffer, &mapinfo);
+
+            frame->out_buffer = gst_buffer_copy_region (frame->buffer,
+                GST_BUFFER_COPY_ALL, 0, seg.offset - 2);
+            GST_MINI_OBJECT_FLAGS (frame->out_buffer) |=
+                GST_BUFFER_FLAG_CORRUPTED;
+
+            GST_DEBUG_OBJECT (parse, "Push a frame without EOI, size %d",
+                seg.offset - 2);
+            return gst_jpeg_parse_finish_frame (parse, frame, seg.offset - 2);
+          }
+
+          gst_jpeg_parse_reset (parse);
+          parse->state |= GST_JPEG_PARSER_STATE_GOT_SOI;
+          /* unset tags */
+          gst_base_parse_merge_tags (bparse, NULL, GST_TAG_MERGE_UNDEFINED);
+
           *skipsize = offset - 2;
           GST_DEBUG_OBJECT (parse, "skipping %d bytes before SOI", *skipsize);
           parse->last_offset = 2;
           goto beach;
         }
-        break;
-      case GST_JPEG_MARKER_EOI:{
-        GstFlowReturn ret;
 
+        /* unset tags */
+        gst_base_parse_merge_tags (bparse, NULL, GST_TAG_MERGE_UNDEFINED);
+        parse->state |= GST_JPEG_PARSER_STATE_GOT_SOI;
+        break;
+      case GST_JPEG_MARKER_EOI:
         gst_buffer_unmap (frame->buffer, &mapinfo);
-
-        if (parse->tags) {
-          gst_base_parse_merge_tags (bparse, parse->tags,
-              GST_TAG_MERGE_REPLACE);
-        }
-
-        ret = gst_jpeg_parse_push_frame (parse, frame, seg.offset);
-        gst_jpeg_parse_reset (parse);
-
-        return ret;
-      }
+        return gst_jpeg_parse_finish_frame (parse, frame, seg.offset);
+        break;
       case GST_JPEG_MARKER_SOS:
         if (!valid_state (parse->state, GST_JPEG_PARSER_STATE_GOT_SOF))
           GST_WARNING_OBJECT (parse, "SOS marker without SOF one");