3 * jpegparse: a parser for JPEG streams
5 * Copyright (C) <2009> Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
6 * <2022> Víctor Manuel Jáquez Leal <vjaquez@igalia.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
25 * SECTION:element-jpegparse
27 * @short_description: JPEG parser
29 * Parses a JPEG stream into JPEG images. It looks for EOI boundaries to
30 * split a continuous stream into single-frame buffers. Also reads the
31 * image header searching for image properties such as width and height
32 * among others. Jpegparse can also extract metadata (e.g. xmp).
34 * ## Example launch line
36 * gst-launch-1.0 -v souphttpsrc location=... ! jpegparse ! matroskamux ! filesink location=...
38 * The above pipeline fetches a motion JPEG stream from an IP camera over
39 * HTTP and stores it in a matroska file.
42 /* FIXME: output plain JFIF APP marker only. This provides best code reuse.
43 * JPEG decoders would not need to handle this part anymore. Also when remuxing
44 * (... ! jpegparse ! ... ! jifmux ! ...) metadata consolidation would be
49 * + APP2 -- ICC color profile
50 * + APP3 -- meta (same as exif)
51 * + APP12 -- Photoshop Save for Web: Ducky / Picture info
52 * + APP13 -- Adobe IRB
53 * + check for interlaced mjpeg
61 #include <gst/base/gstbytereader.h>
62 #include <gst/codecparsers/gstjpegparser.h>
63 #include <gst/tag/tag.h>
64 #include <gst/video/video.h>
66 #include "gstjpegparse.h"
70 GST_JPEG_PARSER_STATE_GOT_SOI = 1 << 0,
71 GST_JPEG_PARSER_STATE_GOT_SOF = 1 << 1,
72 GST_JPEG_PARSER_STATE_GOT_SOS = 1 << 2,
73 GST_JPEG_PARSER_STATE_GOT_JFIF = 1 << 3,
74 GST_JPEG_PARSER_STATE_GOT_ADOBE = 1 << 4,
76 GST_JPEG_PARSER_STATE_VALID_PICTURE = (GST_JPEG_PARSER_STATE_GOT_SOI |
77 GST_JPEG_PARSER_STATE_GOT_SOF | GST_JPEG_PARSER_STATE_GOT_SOS),
80 static GstStaticPadTemplate gst_jpeg_parse_src_pad_template =
81 GST_STATIC_PAD_TEMPLATE ("src",
84 GST_STATIC_CAPS ("image/jpeg, "
85 "framerate = (fraction) [ 0/1, MAX ], " "parsed = (boolean) true")
88 static GstStaticPadTemplate gst_jpeg_parse_sink_pad_template =
89 GST_STATIC_PAD_TEMPLATE ("sink",
92 GST_STATIC_CAPS ("image/jpeg")
95 GST_DEBUG_CATEGORY_STATIC (jpeg_parse_debug);
96 #define GST_CAT_DEFAULT jpeg_parse_debug
99 gst_jpeg_parse_handle_frame (GstBaseParse * bparse, GstBaseParseFrame * frame,
101 static gboolean gst_jpeg_parse_set_sink_caps (GstBaseParse * parse,
103 static gboolean gst_jpeg_parse_sink_event (GstBaseParse * parse,
105 static gboolean gst_jpeg_parse_start (GstBaseParse * parse);
106 static gboolean gst_jpeg_parse_stop (GstBaseParse * parse);
108 #define gst_jpeg_parse_parent_class parent_class
109 G_DEFINE_TYPE (GstJpegParse, gst_jpeg_parse, GST_TYPE_BASE_PARSE);
110 GST_ELEMENT_REGISTER_DEFINE (jpegparse, "jpegparse", GST_RANK_NONE,
111 GST_TYPE_JPEG_PARSE);
113 enum GstJPEGColorspace
115 GST_JPEG_COLORSPACE_NONE,
116 GST_JPEG_COLORSPACE_RGB,
117 GST_JPEG_COLORSPACE_YUV,
118 GST_JPEG_COLORSPACE_GRAY,
119 GST_JPEG_COLORSPACE_CMYK,
120 GST_JPEG_COLORSPACE_YCCK,
123 static const gchar *gst_jpeg_colorspace_strings[] = {
124 [GST_JPEG_COLORSPACE_NONE] = NULL,
125 [GST_JPEG_COLORSPACE_RGB] = "sRGB",
126 [GST_JPEG_COLORSPACE_YUV] = "sYUV",
127 [GST_JPEG_COLORSPACE_GRAY] = "GRAY",
128 [GST_JPEG_COLORSPACE_CMYK] = "CMYK",
129 [GST_JPEG_COLORSPACE_YCCK] = "YCCK",
134 GST_JPEG_SAMPLING_NONE,
135 GST_JPEG_SAMPLING_RGB,
136 GST_JPEG_SAMPLING_BGR,
137 GST_JPEG_SAMPLING_YBR444,
138 GST_JPEG_SAMPLING_YBR422,
139 GST_JPEG_SAMPLING_YBR420,
140 GST_JPEG_SAMPLING_YBR440,
141 GST_JPEG_SAMPLING_YBR410,
142 GST_JPEG_SAMPLING_YBR411,
143 GST_JPEG_SAMPLING_GRAYSCALE,
146 static const gchar *gst_jpeg_sampling_strings[] = {
147 [GST_JPEG_SAMPLING_NONE] = NULL,
148 [GST_JPEG_SAMPLING_RGB] = "RGB",
149 [GST_JPEG_SAMPLING_BGR] = "BGR",
150 [GST_JPEG_SAMPLING_YBR444] = "YCbCr-4:4:4",
151 [GST_JPEG_SAMPLING_YBR422] = "YCbCr-4:2:2",
152 [GST_JPEG_SAMPLING_YBR420] = "YCbCr-4:2:0",
153 [GST_JPEG_SAMPLING_YBR440] = "YCbCr-4:4:0",
154 [GST_JPEG_SAMPLING_YBR410] = "YCbCr-4:1:0",
155 [GST_JPEG_SAMPLING_YBR411] = "YCbCr-4:1:1",
156 [GST_JPEG_SAMPLING_GRAYSCALE] = "GRAYSCALE",
160 gst_jpeg_parse_class_init (GstJpegParseClass * klass)
162 GstBaseParseClass *gstbaseparse_class;
163 GstElementClass *gstelement_class;
165 gstbaseparse_class = (GstBaseParseClass *) klass;
166 gstelement_class = (GstElementClass *) klass;
168 gstbaseparse_class->start = gst_jpeg_parse_start;
169 gstbaseparse_class->stop = gst_jpeg_parse_stop;
170 gstbaseparse_class->set_sink_caps = gst_jpeg_parse_set_sink_caps;
171 gstbaseparse_class->sink_event = gst_jpeg_parse_sink_event;
172 gstbaseparse_class->handle_frame = gst_jpeg_parse_handle_frame;
174 gst_element_class_add_static_pad_template (gstelement_class,
175 &gst_jpeg_parse_src_pad_template);
176 gst_element_class_add_static_pad_template (gstelement_class,
177 &gst_jpeg_parse_sink_pad_template);
179 gst_element_class_set_static_metadata (gstelement_class,
180 "JPEG stream parser",
181 "Codec/Parser/Image",
182 "Parse JPEG images into single-frame buffers",
183 "Víctor Jáquez <vjaquez@igalia.com>");
185 GST_DEBUG_CATEGORY_INIT (jpeg_parse_debug, "jpegparse", 0, "JPEG parser");
189 gst_jpeg_parse_init (GstJpegParse * parse)
195 gst_jpeg_parse_set_sink_caps (GstBaseParse * bparse, GstCaps * caps)
197 GstJpegParse *parse = GST_JPEG_PARSE_CAST (bparse);
198 GstStructure *s = gst_caps_get_structure (caps, 0);
200 GST_DEBUG_OBJECT (parse, "get sink caps %" GST_PTR_FORMAT, caps);
202 gst_structure_get_fraction (s, "framerate",
203 &parse->framerate_numerator, &parse->framerate_denominator);
208 static inline gboolean
209 valid_state (guint state, guint ref_state)
211 return (state & ref_state) == ref_state;
214 /* https://zpl.fi/chroma-subsampling-and-jpeg-sampling-factors/ */
220 enum GstJPEGSampling sampling;
221 } subsampling_map[] = {
222 {{1, 1, 1}, {1, 1, 1}, GST_JPEG_SAMPLING_YBR444},
223 {{2, 2, 2}, {1, 1, 1}, GST_JPEG_SAMPLING_YBR444},
224 {{3, 3, 3}, {1, 1, 1}, GST_JPEG_SAMPLING_YBR444},
225 {{1, 1, 1}, {2, 2, 2}, GST_JPEG_SAMPLING_YBR444},
226 {{1, 1, 1}, {3, 3, 3}, GST_JPEG_SAMPLING_YBR444},
227 {{1, 1, 1}, {2, 1, 1}, GST_JPEG_SAMPLING_YBR440},
228 {{2, 2, 2}, {2, 1, 1}, GST_JPEG_SAMPLING_YBR440},
229 {{1, 1, 1}, {4, 2, 2}, GST_JPEG_SAMPLING_YBR440},
230 {{2, 1, 1}, {1, 1, 1}, GST_JPEG_SAMPLING_YBR422},
231 {{2, 1, 1}, {2, 2, 2}, GST_JPEG_SAMPLING_YBR422},
232 {{4, 2, 2}, {1, 1, 1}, GST_JPEG_SAMPLING_YBR422},
233 {{2, 1, 1}, {2, 1, 1}, GST_JPEG_SAMPLING_YBR420},
234 {{4, 1, 1}, {1, 1, 1}, GST_JPEG_SAMPLING_YBR411},
235 {{4, 1, 1}, {2, 1, 1}, GST_JPEG_SAMPLING_YBR410},
240 yuv_sampling (GstJpegFrameHdr * frame_hdr)
242 int i, h0, h1, h2, v0, v1, v2;
244 g_return_val_if_fail (frame_hdr->num_components == 3, GST_JPEG_SAMPLING_NONE);
246 h0 = frame_hdr->components[0].horizontal_factor;
247 h1 = frame_hdr->components[1].horizontal_factor;
248 h2 = frame_hdr->components[2].horizontal_factor;
249 v0 = frame_hdr->components[0].vertical_factor;
250 v1 = frame_hdr->components[1].vertical_factor;
251 v2 = frame_hdr->components[2].vertical_factor;
253 for (i = 0; i < G_N_ELEMENTS (subsampling_map); i++) {
254 if (subsampling_map[i].h[0] == h0
255 && subsampling_map[i].h[1] == h1 && subsampling_map[i].h[2] == h2
256 && subsampling_map[i].v[0] == v0
257 && subsampling_map[i].v[1] == v1 && subsampling_map[i].v[2] == v2)
258 return subsampling_map[i].sampling;
261 return GST_JPEG_SAMPLING_NONE;
265 colorspace_to_string (enum GstJPEGColorspace colorspace)
267 return gst_jpeg_colorspace_strings[colorspace];
270 /* https://entropymine.wordpress.com/2018/10/22/how-is-a-jpeg-images-color-type-determined/ */
271 /* T-REC-T.872-201206 6.1 Colour encodings and associated values to define white and black */
273 gst_jpeg_parse_sof (GstJpegParse * parse, GstJpegSegment * seg)
275 GstJpegFrameHdr hdr = { 0, };
277 if (!gst_jpeg_segment_parse_frame_header (seg, &hdr)) {
281 parse->width = hdr.width;
282 parse->height = hdr.height;
284 parse->colorspace = GST_JPEG_COLORSPACE_NONE;
285 parse->sampling = GST_JPEG_SAMPLING_NONE;
287 switch (hdr.num_components) {
289 parse->colorspace = GST_JPEG_COLORSPACE_GRAY;
290 parse->sampling = GST_JPEG_SAMPLING_GRAYSCALE;
293 if (valid_state (parse->state, GST_JPEG_PARSER_STATE_GOT_JFIF)) {
294 parse->colorspace = GST_JPEG_COLORSPACE_YUV;
295 parse->sampling = yuv_sampling (&hdr);
297 if (valid_state (parse->state, GST_JPEG_PARSER_STATE_GOT_ADOBE)) {
298 if (parse->adobe_transform == 0) {
299 parse->colorspace = GST_JPEG_COLORSPACE_RGB;
300 parse->sampling = GST_JPEG_SAMPLING_RGB;
301 } else if (parse->adobe_transform == 1) {
302 parse->colorspace = GST_JPEG_COLORSPACE_YUV;;
303 parse->sampling = yuv_sampling (&hdr);
305 GST_DEBUG_OBJECT (parse, "Unknown Adobe color transform code");
306 parse->colorspace = GST_JPEG_COLORSPACE_YUV;;
307 parse->sampling = yuv_sampling (&hdr);
310 int cid0, cid1, cid2;
312 cid0 = hdr.components[0].identifier;
313 cid1 = hdr.components[1].identifier;
314 cid2 = hdr.components[2].identifier;
316 if (cid0 == 1 && cid1 == 2 && cid2 == 3) {
317 parse->colorspace = GST_JPEG_COLORSPACE_YUV;
318 parse->sampling = yuv_sampling (&hdr);
319 } else if (cid0 == 'R' && cid1 == 'G' && cid2 == 'B') {
320 parse->colorspace = GST_JPEG_COLORSPACE_RGB;
321 parse->sampling = GST_JPEG_SAMPLING_RGB;
323 GST_DEBUG_OBJECT (parse, "Unrecognized component IDs");
324 parse->colorspace = GST_JPEG_COLORSPACE_YUV;
325 parse->sampling = yuv_sampling (&hdr);
331 if (valid_state (parse->state, GST_JPEG_PARSER_STATE_GOT_ADOBE)) {
332 if (parse->adobe_transform == 0) {
333 parse->colorspace = GST_JPEG_COLORSPACE_CMYK;
334 } else if (parse->adobe_transform == 2) {
335 parse->colorspace = GST_JPEG_COLORSPACE_YCCK;
337 GST_DEBUG_OBJECT (parse, "Unknown Adobe color transform code");
338 parse->colorspace = GST_JPEG_COLORSPACE_YCCK;
341 parse->colorspace = GST_JPEG_COLORSPACE_CMYK;
345 GST_WARNING_OBJECT (parse, "Unknown color space");
349 GST_INFO_OBJECT (parse, "SOF [%dx%d] %d comp - %s", parse->width,
350 parse->height, hdr.num_components,
351 GST_STR_NULL (colorspace_to_string (parse->colorspace)));
355 static inline GstTagList *
356 get_tag_list (GstJpegParse * parse)
359 parse->tags = gst_tag_list_new_empty ();
364 gst_jpeg_parse_app0 (GstJpegParse * parse, GstJpegSegment * seg)
366 GstByteReader reader;
371 if (seg->size < 6) /* less than 6 means no id string */
374 gst_byte_reader_init (&reader, seg->data + seg->offset, seg->size);
375 gst_byte_reader_skip_unchecked (&reader, 2);
377 if (!gst_byte_reader_get_string_utf8 (&reader, &id_str))
380 if (!valid_state (parse->state, GST_JPEG_PARSER_STATE_GOT_JFIF)
381 && g_strcmp0 (id_str, "JFIF") == 0) {
383 parse->state |= GST_JPEG_PARSER_STATE_GOT_JFIF;
386 gst_byte_reader_skip_unchecked (&reader, 2);
389 if (!gst_byte_reader_get_uint8 (&reader, &unit))
393 if (!gst_byte_reader_get_uint16_be (&reader, &xd))
396 if (!gst_byte_reader_get_uint16_be (&reader, &yd))
400 if (!gst_byte_reader_get_uint8 (&reader, &xt))
403 if (!gst_byte_reader_get_uint8 (&reader, &yt))
407 /* no units, X and Y specify the pixel aspect ratio */
408 parse->x_density = xd;
409 parse->y_density = yd;
410 } else if (unit == 1 || unit == 2) {
411 /* tag pixel per inches */
412 double hppi = xd, vppi = yd;
420 gst_tag_register_musicbrainz_tags ();
421 gst_tag_list_add (get_tag_list (parse), GST_TAG_MERGE_REPLACE,
422 GST_TAG_IMAGE_HORIZONTAL_PPI, hppi, GST_TAG_IMAGE_VERTICAL_PPI, vppi,
426 if (xt > 0 && yt > 0)
427 GST_FIXME_OBJECT (parse, "embedded thumbnail ignored");
433 if (g_strcmp0 (id_str, "JFXX") == 0) {
434 if (!valid_state (parse->state, GST_JPEG_PARSER_STATE_GOT_JFIF))
440 /* https://exiftool.org/TagNames/JPEG.html#AVI1 */
441 if (g_strcmp0 (id_str, "AVI1") == 0) {
443 if (!gst_byte_reader_get_uint8 (&reader, &unit))
446 /* TODO: update caps for interlaced MJPEG */
447 GST_DEBUG_OBJECT (parse, "MJPEG interleaved field: %d", unit);
452 GST_DEBUG_OBJECT (parse, "Unhandled app0: %s", id_str);
461 GstTagList *(*tag_func) (GstBuffer * buff);
463 {"Exif", gst_tag_list_from_exif_buffer_with_tiff_header},
464 {"http://ns.adobe.com/xap/1.0/", gst_tag_list_from_xmp_buffer},
469 gst_jpeg_parse_app1 (GstJpegParse * parse, GstJpegSegment * seg)
471 GstByteReader reader;
478 if (seg->size < 6) /* less than 6 means no id string */
481 gst_byte_reader_init (&reader, seg->data + seg->offset, seg->size);
482 gst_byte_reader_skip_unchecked (&reader, 2);
484 if (!gst_byte_reader_get_string_utf8 (&reader, &id_str))
487 for (i = 0; i < G_N_ELEMENTS (TagMap); i++) {
488 if (!g_str_has_suffix (id_str, TagMap[i].suffix))
491 /* skip NUL only for Exif */
493 if (!gst_byte_reader_skip (&reader, 1))
497 size = gst_byte_reader_get_remaining (&reader);
499 if (!gst_byte_reader_get_data (&reader, size, &data))
502 buf = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY,
503 (gpointer) data, size, 0, size, NULL, NULL);
508 tags = TagMap[i].tag_func (buf);
509 gst_buffer_unref (buf);
512 GST_LOG_OBJECT (parse, "parsed marker %x: '%s' %" GST_PTR_FORMAT,
513 GST_JPEG_MARKER_APP1, id_str, tags);
514 gst_tag_list_insert (get_tag_list (parse), tags, GST_TAG_MERGE_REPLACE);
515 gst_tag_list_unref (tags);
517 GST_INFO_OBJECT (parse, "failed to parse %s: %s", id_str, data);
525 GST_DEBUG_OBJECT (parse, "Unhandled app1: %s", id_str);
531 gst_jpeg_parse_app14 (GstJpegParse * parse, GstJpegSegment * seg)
533 GstByteReader reader;
537 if (seg->size < 6) /* less than 6 means no id string */
540 gst_byte_reader_init (&reader, seg->data + seg->offset, seg->size);
541 gst_byte_reader_skip_unchecked (&reader, 2);
543 if (!gst_byte_reader_get_string_utf8 (&reader, &id_str))
546 if (!g_str_has_prefix (id_str, "Adobe")) {
547 GST_DEBUG_OBJECT (parse, "Unhandled app14: %s", id_str);
551 /* skip version and flags */
552 if (!gst_byte_reader_skip (&reader, 6))
555 parse->state |= GST_JPEG_PARSER_STATE_GOT_ADOBE;
557 /* transform bit might not exist */
558 if (!gst_byte_reader_get_uint8 (&reader, &transform))
561 parse->adobe_transform = transform;
565 static inline gchar *
566 get_utf8_from_data (const guint8 * data, guint16 size)
568 const gchar *env_vars[] = { "GST_JPEG_TAG_ENCODING",
569 "GST_TAG_ENCODING", NULL
571 const char *str = (gchar *) data;
573 return gst_tag_freeform_string_to_utf8 (str, size, env_vars);
576 /* read comment and post as tag */
577 static inline gboolean
578 gst_jpeg_parse_com (GstJpegParse * parse, GstJpegSegment * seg)
580 GstByteReader reader;
581 const guint8 *data = NULL;
585 gst_byte_reader_init (&reader, seg->data + seg->offset, seg->size);
586 gst_byte_reader_skip_unchecked (&reader, 2);
588 size = gst_byte_reader_get_remaining (&reader);
590 if (!gst_byte_reader_get_data (&reader, size, &data))
593 comment = get_utf8_from_data (data, size);
597 GST_INFO_OBJECT (parse, "comment found: %s", comment);
598 gst_tag_list_add (get_tag_list (parse), GST_TAG_MERGE_REPLACE,
599 GST_TAG_COMMENT, comment, NULL);
606 gst_jpeg_parse_reset (GstJpegParse * parse)
610 parse->last_offset = 0;
613 parse->adobe_transform = 0;
614 parse->x_density = 0;
615 parse->y_density = 0;
618 gst_tag_list_unref (parse->tags);
624 sampling_to_string (enum GstJPEGSampling sampling)
626 return gst_jpeg_sampling_strings[sampling];
630 gst_jpeg_parse_set_new_caps (GstJpegParse * parse)
635 caps = gst_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
638 if (parse->width > 0)
639 gst_caps_set_simple (caps, "width", G_TYPE_INT, parse->width, NULL);
640 if (parse->width > 0)
641 gst_caps_set_simple (caps, "height", G_TYPE_INT, parse->height, NULL);
643 gst_caps_set_simple (caps, "sof-marker", G_TYPE_INT, parse->sof, NULL);
644 if (parse->colorspace != GST_JPEG_COLORSPACE_NONE) {
645 gst_caps_set_simple (caps, "colorspace", G_TYPE_STRING,
646 colorspace_to_string (parse->colorspace), NULL);
648 if (parse->sampling != GST_JPEG_SAMPLING_NONE) {
649 gst_caps_set_simple (caps, "sampling", G_TYPE_STRING,
650 sampling_to_string (parse->sampling), NULL);
653 gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION,
654 parse->framerate_numerator, parse->framerate_denominator, NULL);
656 if (parse->x_density > 0 && parse->y_density > 0) {
657 gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
658 parse->x_density, parse->y_density, NULL);
661 if (parse->prev_caps && gst_caps_is_equal_fixed (caps, parse->prev_caps)) {
662 gst_caps_unref (caps);
666 GST_DEBUG_OBJECT (parse,
667 "setting downstream caps on %s:%s to %" GST_PTR_FORMAT,
668 GST_DEBUG_PAD_NAME (GST_BASE_PARSE_SRC_PAD (parse)), caps);
669 res = gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps);
671 gst_caps_replace (&parse->prev_caps, caps);
672 gst_caps_unref (caps);
679 gst_jpeg_parse_finish_frame (GstJpegParse * parse, GstBaseParseFrame * frame,
682 GstBaseParse *bparse = GST_BASE_PARSE (parse);
686 gst_base_parse_merge_tags (bparse, parse->tags, GST_TAG_MERGE_REPLACE);
688 if (!gst_jpeg_parse_set_new_caps (parse))
689 return GST_FLOW_ERROR;
691 if (!valid_state (parse->state, GST_JPEG_PARSER_STATE_VALID_PICTURE)) {
692 /* this validation breaks unit tests */
693 /* frame->flags |= GST_BASE_PARSE_FRAME_FLAG_DROP; */
694 GST_WARNING_OBJECT (parse, "Potentially invalid picture");
697 ret = gst_base_parse_finish_frame (bparse, frame, size);
699 gst_jpeg_parse_reset (parse);
705 gst_jpeg_parse_handle_frame (GstBaseParse * bparse, GstBaseParseFrame * frame,
708 GstJpegParse *parse = GST_JPEG_PARSE_CAST (bparse);
710 GstJpegMarker marker;
714 if (!gst_buffer_map (frame->buffer, &mapinfo, GST_MAP_READ))
715 return GST_FLOW_ERROR;
717 offset = parse->last_offset;
719 offset -= 1; /* it migth be in the middle marker */
721 while (offset < mapinfo.size) {
722 if (!gst_jpeg_parse (&seg, mapinfo.data, mapinfo.size, offset)) {
723 if (!valid_state (parse->state, GST_JPEG_PARSER_STATE_GOT_SOI)) {
724 /* Skip any garbage until SOI */
725 *skipsize = mapinfo.size;
726 GST_INFO_OBJECT (parse, "skipping %d bytes", *skipsize);
728 /* Accept anything after SOI */
729 parse->last_offset = mapinfo.size;
737 if (!valid_state (parse->state, GST_JPEG_PARSER_STATE_GOT_SOI)
738 && marker != GST_JPEG_MARKER_SOI)
741 /* check if the whole segment is available */
742 if (offset + seg.size > mapinfo.size) {
743 GST_DEBUG_OBJECT (parse, "incomplete segment: %x [offset %d]", marker,
745 parse->last_offset = offset - 2;
751 GST_LOG_OBJECT (parse, "marker found: %x [offset %d / size %"
752 G_GSSIZE_FORMAT "]", marker, seg.offset, seg.size);
755 case GST_JPEG_MARKER_SOI:
756 /* This means that new SOI comes without an previous EOI. */
758 /* If already some data segment parsed, push it as a frame. */
759 if (valid_state (parse->state, GST_JPEG_PARSER_STATE_GOT_SOS)) {
760 gst_buffer_unmap (frame->buffer, &mapinfo);
762 frame->out_buffer = gst_buffer_copy_region (frame->buffer,
763 GST_BUFFER_COPY_ALL, 0, seg.offset - 2);
764 GST_MINI_OBJECT_FLAGS (frame->out_buffer) |=
765 GST_BUFFER_FLAG_CORRUPTED;
767 GST_WARNING_OBJECT (parse, "Push a frame without EOI, size %d",
769 return gst_jpeg_parse_finish_frame (parse, frame, seg.offset - 2);
772 gst_jpeg_parse_reset (parse);
773 parse->state |= GST_JPEG_PARSER_STATE_GOT_SOI;
775 gst_base_parse_merge_tags (bparse, NULL, GST_TAG_MERGE_UNDEFINED);
777 *skipsize = offset - 2;
778 GST_DEBUG_OBJECT (parse, "skipping %d bytes before SOI", *skipsize);
779 parse->last_offset = 2;
784 gst_base_parse_merge_tags (bparse, NULL, GST_TAG_MERGE_UNDEFINED);
785 parse->state |= GST_JPEG_PARSER_STATE_GOT_SOI;
787 case GST_JPEG_MARKER_EOI:
788 gst_buffer_unmap (frame->buffer, &mapinfo);
789 return gst_jpeg_parse_finish_frame (parse, frame, seg.offset);
791 case GST_JPEG_MARKER_SOS:
792 if (!valid_state (parse->state, GST_JPEG_PARSER_STATE_GOT_SOF))
793 GST_WARNING_OBJECT (parse, "SOS marker without SOF one");
794 parse->state |= GST_JPEG_PARSER_STATE_GOT_SOS;
796 case GST_JPEG_MARKER_COM:
797 if (!gst_jpeg_parse_com (parse, &seg)) {
798 GST_ELEMENT_WARNING (parse, STREAM, FORMAT,
799 ("Failed to parse com segment"), ("Invalid data"));
802 case GST_JPEG_MARKER_APP0:
803 if (!gst_jpeg_parse_app0 (parse, &seg)) {
804 GST_ELEMENT_WARNING (parse, STREAM, FORMAT,
805 ("Failed to parse app0 segment"), ("Invalid data"));
808 case GST_JPEG_MARKER_APP1:
809 if (!gst_jpeg_parse_app1 (parse, &seg)) {
810 GST_ELEMENT_WARNING (parse, STREAM, FORMAT,
811 ("Failed to parse app1 segment"), ("Invalid data"));
814 case GST_JPEG_MARKER_APP14:
815 if (!gst_jpeg_parse_app14 (parse, &seg)) {
816 GST_ELEMENT_WARNING (parse, STREAM, FORMAT,
817 ("Failed to parse app14 segment"), ("Invalid data"));
820 case GST_JPEG_MARKER_DHT:
821 case GST_JPEG_MARKER_DAC:
822 /* to avoid break the below SOF interval */
826 if (marker >= GST_JPEG_MARKER_SOF_MIN &&
827 marker <= GST_JPEG_MARKER_SOF_MAX) {
828 if (!valid_state (parse->state, GST_JPEG_PARSER_STATE_GOT_SOF)
829 && gst_jpeg_parse_sof (parse, &seg)) {
830 parse->state |= GST_JPEG_PARSER_STATE_GOT_SOF;
831 parse->sof = marker - 0xc0;
833 GST_ELEMENT_ERROR (parse, STREAM, FORMAT,
834 ("Duplicated or bad SOF marker"), (NULL));
835 gst_buffer_unmap (frame->buffer, &mapinfo);
836 gst_jpeg_parse_reset (parse);
837 return GST_FLOW_ERROR;
844 parse->last_offset = offset;
847 gst_buffer_unmap (frame->buffer, &mapinfo);
852 gst_jpeg_parse_sink_event (GstBaseParse * bparse, GstEvent * event)
854 GstJpegParse *parse = GST_JPEG_PARSE_CAST (bparse);
856 GST_DEBUG_OBJECT (parse, "event : %s", GST_EVENT_TYPE_NAME (event));
858 switch (GST_EVENT_TYPE (event)) {
859 case GST_EVENT_FLUSH_STOP:
860 gst_jpeg_parse_reset (parse);
866 return GST_BASE_PARSE_CLASS (parent_class)->sink_event (bparse, event);
870 gst_jpeg_parse_start (GstBaseParse * bparse)
872 GstJpegParse *parse = GST_JPEG_PARSE_CAST (bparse);
874 parse->framerate_numerator = 0;
875 parse->framerate_denominator = 1;
877 gst_jpeg_parse_reset (parse);
879 gst_base_parse_set_min_frame_size (bparse, 2);
885 gst_jpeg_parse_stop (GstBaseParse * bparse)
887 GstJpegParse *parse = GST_JPEG_PARSE_CAST (bparse);
890 gst_tag_list_unref (parse->tags);
893 gst_clear_caps (&parse->prev_caps);