1 /* GStreamer
2  *
3  * jpegparse: a parser for JPEG streams
4  *
5  * Copyright (C) <2009> Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
6  *                      Víctor Manuel Jáquez Leal <vjaquez@igalia.com>
7  *
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.
12  *
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.
17  *
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.
22  */
23 
24 /**
25  * SECTION:element-jpegparse
26  * @title: jpegparse
27  * @short_description: JPEG parser
28  *
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).
33  *
34  * ## Example launch line
35  * |[
36  * gst-launch-1.0 -v souphttpsrc location=... ! jpegparse ! matroskamux ! filesink location=...
37  * ]|
38  * The above pipeline fetches a motion JPEG stream from an IP camera over
39  * HTTP and stores it in a matroska file.
40  *
41  */
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
45  * easier.
46  */
47 #ifdef HAVE_CONFIG_H
48 #include <config.h>
49 #endif
50 
51 #include <string.h>
52 #include <gst/base/gstbytereader.h>
53 #include <gst/tag/tag.h>
54 
55 #include "gstjpegparse.h"
56 
57 static GstStaticPadTemplate gst_jpeg_parse_src_pad_template =
58 GST_STATIC_PAD_TEMPLATE ("src",
59     GST_PAD_SRC,
60     GST_PAD_ALWAYS,
61     GST_STATIC_CAPS ("image/jpeg, "
62         "format = (string) { I420, Y41B, UYVY, YV12 }, "
63         "width = (int) [ 0, MAX ],"
64         "height = (int) [ 0, MAX ], "
65         "framerate = (fraction) [ 0/1, MAX ], " "parsed = (boolean) true")
66     );
67 
68 static GstStaticPadTemplate gst_jpeg_parse_sink_pad_template =
69 GST_STATIC_PAD_TEMPLATE ("sink",
70     GST_PAD_SINK,
71     GST_PAD_ALWAYS,
72     GST_STATIC_CAPS ("image/jpeg")
73     );
74 
75 GST_DEBUG_CATEGORY_STATIC (jpeg_parse_debug);
76 #define GST_CAT_DEFAULT jpeg_parse_debug
77 
78 static GstFlowReturn
79 gst_jpeg_parse_handle_frame (GstBaseParse * bparse, GstBaseParseFrame * frame,
80     gint * skipsize);
81 static gboolean gst_jpeg_parse_set_sink_caps (GstBaseParse * parse,
82     GstCaps * caps);
83 static gboolean gst_jpeg_parse_sink_event (GstBaseParse * parse,
84     GstEvent * event);
85 static gboolean gst_jpeg_parse_start (GstBaseParse * parse);
86 static gboolean gst_jpeg_parse_stop (GstBaseParse * parse);
87 static GstFlowReturn gst_jpeg_parse_pre_push_frame (GstBaseParse * bparse,
88     GstBaseParseFrame * frame);
89 
90 #define gst_jpeg_parse_parent_class parent_class
91 G_DEFINE_TYPE (GstJpegParse, gst_jpeg_parse, GST_TYPE_BASE_PARSE);
92 
93 static void
gst_jpeg_parse_class_init(GstJpegParseClass * klass)94 gst_jpeg_parse_class_init (GstJpegParseClass * klass)
95 {
96   GstBaseParseClass *gstbaseparse_class;
97   GstElementClass *gstelement_class;
98 
99   gstbaseparse_class = (GstBaseParseClass *) klass;
100   gstelement_class = (GstElementClass *) klass;
101 
102   gstbaseparse_class->start = gst_jpeg_parse_start;
103   gstbaseparse_class->stop = gst_jpeg_parse_stop;
104   gstbaseparse_class->set_sink_caps = gst_jpeg_parse_set_sink_caps;
105   gstbaseparse_class->sink_event = gst_jpeg_parse_sink_event;
106   gstbaseparse_class->handle_frame = gst_jpeg_parse_handle_frame;
107   gstbaseparse_class->pre_push_frame = gst_jpeg_parse_pre_push_frame;
108 
109   gst_element_class_add_static_pad_template (gstelement_class,
110       &gst_jpeg_parse_src_pad_template);
111   gst_element_class_add_static_pad_template (gstelement_class,
112       &gst_jpeg_parse_sink_pad_template);
113 
114   gst_element_class_set_static_metadata (gstelement_class,
115       "JPEG stream parser",
116       "Video/Parser",
117       "Parse JPEG images into single-frame buffers",
118       "Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>");
119 
120   GST_DEBUG_CATEGORY_INIT (jpeg_parse_debug, "jpegparse", 0, "JPEG parser");
121 }
122 
123 static void
gst_jpeg_parse_init(GstJpegParse * parse)124 gst_jpeg_parse_init (GstJpegParse * parse)
125 {
126   parse->next_ts = GST_CLOCK_TIME_NONE;
127 }
128 
129 static gboolean
gst_jpeg_parse_set_sink_caps(GstBaseParse * bparse,GstCaps * caps)130 gst_jpeg_parse_set_sink_caps (GstBaseParse * bparse, GstCaps * caps)
131 {
132   GstJpegParse *parse = GST_JPEG_PARSE_CAST (bparse);
133   GstStructure *s = gst_caps_get_structure (caps, 0);
134   const GValue *framerate;
135 
136   if ((framerate = gst_structure_get_value (s, "framerate")) != NULL) {
137     if (GST_VALUE_HOLDS_FRACTION (framerate)) {
138       parse->framerate_numerator = gst_value_get_fraction_numerator (framerate);
139       parse->framerate_denominator =
140           gst_value_get_fraction_denominator (framerate);
141       parse->has_fps = TRUE;
142       GST_DEBUG_OBJECT (parse, "got framerate of %d/%d",
143           parse->framerate_numerator, parse->framerate_denominator);
144     }
145   }
146 
147   return TRUE;
148 }
149 
150 
151 /*
152  * gst_jpeg_parse_skip_to_jpeg_header:
153  * @parse: the parser
154  *
155  * Flush everything until the next JPEG header.  The header is considered
156  * to be the a start marker SOI (0xff 0xd8) followed by any other marker
157  * (0xff ...).
158  *
159  * Returns: TRUE if the header was found, FALSE if more data is needed.
160  */
161 static gboolean
gst_jpeg_parse_skip_to_jpeg_header(GstJpegParse * parse,GstMapInfo * mapinfo,gint * skipsize)162 gst_jpeg_parse_skip_to_jpeg_header (GstJpegParse * parse, GstMapInfo * mapinfo,
163     gint * skipsize)
164 {
165   gboolean ret = TRUE;
166   GstByteReader reader;
167 
168   if (mapinfo->size < 4)
169     return FALSE;
170 
171   gst_byte_reader_init (&reader, mapinfo->data, mapinfo->size);
172 
173   *skipsize = gst_byte_reader_masked_scan_uint32 (&reader, 0xffffff00,
174       0xffd8ff00, 0, mapinfo->size);
175   if (*skipsize == -1) {
176     *skipsize = mapinfo->size - 3;      /* Last 3 bytes + 1 more may match header. */
177     ret = FALSE;
178   }
179   return ret;
180 }
181 
182 static inline gboolean
gst_jpeg_parse_parse_tag_has_entropy_segment(guint8 tag)183 gst_jpeg_parse_parse_tag_has_entropy_segment (guint8 tag)
184 {
185   if (tag == SOS || (tag >= RST0 && tag <= RST7))
186     return TRUE;
187   return FALSE;
188 }
189 
190 /* returns image length in bytes if parsed successfully,
191  * otherwise 0 if more data needed,
192  * if < 0 the absolute value needs to be flushed */
193 static gint
gst_jpeg_parse_get_image_length(GstJpegParse * parse,GstMapInfo * mapinfo)194 gst_jpeg_parse_get_image_length (GstJpegParse * parse, GstMapInfo * mapinfo)
195 {
196   guint size;
197   gboolean resync;
198   gint offset, noffset;
199   GstByteReader reader;
200 
201   size = mapinfo->size;
202   gst_byte_reader_init (&reader, mapinfo->data, mapinfo->size);
203 
204   /* TODO could be removed as previous functions already guarantee this to be
205    * true */
206   /* we expect at least 4 bytes, first of which start marker */
207   if (gst_byte_reader_masked_scan_uint32 (&reader, 0xffff0000, 0xffd80000, 0,
208           4))
209     return 0;
210 
211   GST_DEBUG ("Parsing jpeg image data (%u bytes)", size);
212 
213   GST_DEBUG ("Parse state: offset=%d, resync=%d, entropy len=%d",
214       parse->last_offset, parse->last_resync, parse->last_entropy_len);
215 
216   /* offset is 2 less than actual offset;
217    * - adapter needs at least 4 bytes for scanning,
218    * - start and end marker ensure at least that much
219    */
220   /* resume from state offset */
221   offset = parse->last_offset;
222 
223   while (1) {
224     guint frame_len;
225     guint32 value;
226 
227     noffset =
228         gst_byte_reader_masked_scan_uint32_peek (&reader, 0x0000ff00,
229         0x0000ff00, offset, size - offset, &value);
230     /* lost sync if 0xff marker not where expected */
231     if ((resync = (noffset != offset))) {
232       GST_DEBUG ("Lost sync at 0x%08x, resyncing", offset + 2);
233     }
234     /* may have marker, but could have been resyncng */
235     resync = resync || parse->last_resync;
236     /* Skip over extra 0xff */
237     while ((noffset >= 0) && ((value & 0xff) == 0xff)) {
238       noffset++;
239       noffset =
240           gst_byte_reader_masked_scan_uint32_peek (&reader, 0x0000ff00,
241           0x0000ff00, noffset, size - noffset, &value);
242     }
243     /* enough bytes left for marker? (we need 0xNN after the 0xff) */
244     if (noffset < 0) {
245       GST_DEBUG ("at end of input and no EOI marker found, need more data");
246       goto need_more_data;
247     }
248 
249     /* now lock on the marker we found */
250     offset = noffset;
251     value = value & 0xff;
252     if (value == 0xd9) {
253       GST_DEBUG ("0x%08x: EOI marker", offset + 2);
254       /* clear parse state */
255       parse->last_resync = FALSE;
256       parse->last_offset = 0;
257       return (offset + 4);
258     } else if (value == 0xd8) {
259       /* Skip this frame if we found another SOI marker */
260       GST_DEBUG ("0x%08x: SOI marker before EOI, skipping", offset + 2);
261       /* clear parse state */
262       parse->last_resync = FALSE;
263       parse->last_offset = 0;
264       return -(offset + 2);
265     }
266 
267     if (value >= 0xd0 && value <= 0xd7)
268       frame_len = 0;
269     else {
270       /* peek tag and subsequent length */
271       if (offset + 2 + 4 > size)
272         goto need_more_data;
273       else
274         gst_byte_reader_masked_scan_uint32_peek (&reader, 0x0, 0x0, offset + 2,
275             4, &frame_len);
276       frame_len = frame_len & 0xffff;
277     }
278     GST_DEBUG ("0x%08x: tag %02x, frame_len=%u", offset + 2, value, frame_len);
279     /* the frame length includes the 2 bytes for the length; here we want at
280      * least 2 more bytes at the end for an end marker */
281     if (offset + 2 + 2 + frame_len + 2 > size) {
282       goto need_more_data;
283     }
284 
285     if (gst_jpeg_parse_parse_tag_has_entropy_segment (value)) {
286       guint eseglen = parse->last_entropy_len;
287 
288       GST_DEBUG ("0x%08x: finding entropy segment length", offset + 2);
289       noffset = offset + 2 + frame_len + eseglen;
290       while (1) {
291         noffset = gst_byte_reader_masked_scan_uint32_peek (&reader, 0x0000ff00,
292             0x0000ff00, noffset, size - noffset, &value);
293         if (noffset < 0) {
294           /* need more data */
295           parse->last_entropy_len = size - offset - 4 - frame_len - 2;
296           goto need_more_data;
297         }
298         if ((value & 0xff) != 0x00) {
299           eseglen = noffset - offset - frame_len - 2;
300           break;
301         }
302         noffset++;
303       }
304       parse->last_entropy_len = 0;
305       frame_len += eseglen;
306       GST_DEBUG ("entropy segment length=%u => frame_len=%u", eseglen,
307           frame_len);
308     }
309     if (resync) {
310       /* check if we will still be in sync if we interpret
311        * this as a sync point and skip this frame */
312       noffset = offset + frame_len + 2;
313       noffset =
314           gst_byte_reader_masked_scan_uint32 (&reader, 0x0000ff00, 0x0000ff00,
315           noffset, 4);
316       if (noffset < 0) {
317         /* ignore and continue resyncing until we hit the end
318          * of our data or find a sync point that looks okay */
319         offset++;
320         continue;
321       }
322       GST_DEBUG ("found sync at 0x%x", offset + 2);
323     }
324 
325     offset += frame_len + 2;
326   }
327 
328   /* EXITS */
329 need_more_data:
330   {
331     parse->last_offset = offset;
332     parse->last_resync = resync;
333     return 0;
334   }
335 }
336 
337 static inline gboolean
gst_jpeg_parse_sof(GstJpegParse * parse,GstByteReader * reader)338 gst_jpeg_parse_sof (GstJpegParse * parse, GstByteReader * reader)
339 {
340   guint8 numcomps = 0;          /* Number of components in image
341                                    (1 for gray, 3 for YUV, etc.) */
342   guint8 precision;             /* precision (in bits) for the samples */
343   guint8 compId[3] G_GNUC_UNUSED;       /* unique value identifying each component */
344   guint8 qtId[3] G_GNUC_UNUSED; /* quantization table ID to use for this comp */
345   guint8 blockWidth[3];         /* Array[numComponents] giving the number of
346                                    blocks (horiz) in this component */
347   guint8 blockHeight[3];        /* Same for the vertical part of this component */
348   guint8 i, value = 0;
349   gint temp;
350 
351   /* flush length field */
352   if (!gst_byte_reader_skip (reader, 2))
353     return FALSE;
354 
355   /* Get sample precision */
356   if (!gst_byte_reader_get_uint8 (reader, &precision))
357     return FALSE;
358 
359   /* Get w and h */
360   if (!gst_byte_reader_get_uint16_be (reader, &parse->height))
361     return FALSE;
362   if (!gst_byte_reader_get_uint16_be (reader, &parse->width))
363     return FALSE;
364 
365   /* Get number of components */
366   if (!gst_byte_reader_get_uint8 (reader, &numcomps))
367     return FALSE;
368 
369   if (numcomps > 3)             /* FIXME */
370     return FALSE;
371 
372   /* Get decimation and quantization table id for each component */
373   for (i = 0; i < numcomps; i++) {
374     /* Get component ID number */
375     if (!gst_byte_reader_get_uint8 (reader, &value))
376       return FALSE;
377     compId[i] = value;
378 
379     /* Get decimation */
380     if (!gst_byte_reader_get_uint8 (reader, &value))
381       return FALSE;
382     blockWidth[i] = (value & 0xf0) >> 4;
383     blockHeight[i] = (value & 0x0f);
384 
385     /* Get quantization table id */
386     if (!gst_byte_reader_get_uint8 (reader, &value))
387       return FALSE;
388     qtId[i] = value;
389   }
390 
391   if (numcomps == 1) {
392     /* gray image - no format */
393     parse->format = "";
394   } else if (numcomps == 3) {
395     temp = (blockWidth[0] * blockHeight[0]) / (blockWidth[1] * blockHeight[1]);
396 
397     if (temp == 4 && blockHeight[0] == 2)
398       parse->format = "I420";
399     else if (temp == 4 && blockHeight[0] == 4)
400       parse->format = "Y41B";
401     else if (temp == 2)
402       parse->format = "UYVY";
403     else if (temp == 1)
404       parse->format = "YV12";
405     else
406       parse->format = "";
407   } else {
408     return FALSE;
409   }
410 
411   GST_DEBUG_OBJECT (parse, "Header parsed");
412 
413   return TRUE;
414 }
415 
416 static inline gboolean
gst_jpeg_parse_skip_marker(GstJpegParse * parse,GstByteReader * reader,guint8 marker)417 gst_jpeg_parse_skip_marker (GstJpegParse * parse,
418     GstByteReader * reader, guint8 marker)
419 {
420   guint16 size = 0;
421 
422   if (!gst_byte_reader_get_uint16_be (reader, &size))
423     return FALSE;
424 
425 #ifndef GST_DISABLE_GST_DEBUG
426   /* We'd pry the id of the skipped application segment */
427   if (marker >= APP0 && marker <= APP15) {
428     const gchar *id_str = NULL;
429 
430     if (gst_byte_reader_peek_string_utf8 (reader, &id_str)) {
431       GST_DEBUG_OBJECT (parse, "unhandled marker %x: '%s' skiping %u bytes",
432           marker, id_str ? id_str : "(NULL)", size);
433     } else {
434       GST_DEBUG_OBJECT (parse, "unhandled marker %x skiping %u bytes", marker,
435           size);
436     }
437   }
438 #else
439   GST_DEBUG_OBJECT (parse, "unhandled marker %x skiping %u bytes", marker,
440       size);
441 #endif // GST_DISABLE_GST_DEBUG
442 
443   if (!gst_byte_reader_skip (reader, size - 2))
444     return FALSE;
445 
446   return TRUE;
447 }
448 
449 static inline GstTagList *
get_tag_list(GstJpegParse * parse)450 get_tag_list (GstJpegParse * parse)
451 {
452   if (!parse->tags)
453     parse->tags = gst_tag_list_new_empty ();
454   return parse->tags;
455 }
456 
457 static inline void
extract_and_queue_tags(GstJpegParse * parse,guint size,guint8 * data,GstTagList * (* tag_func)(GstBuffer * buff))458 extract_and_queue_tags (GstJpegParse * parse, guint size, guint8 * data,
459     GstTagList * (*tag_func) (GstBuffer * buff))
460 {
461   GstTagList *tags;
462   GstBuffer *buf;
463 
464   buf = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, data, size, 0,
465       size, NULL, NULL);
466 
467   tags = tag_func (buf);
468   gst_buffer_unref (buf);
469 
470   if (tags) {
471     GstTagList *taglist = parse->tags;
472     if (taglist) {
473       gst_tag_list_insert (taglist, tags, GST_TAG_MERGE_REPLACE);
474       gst_tag_list_unref (tags);
475     } else {
476       parse->tags = tags;
477     }
478     GST_DEBUG_OBJECT (parse, "collected tags: %" GST_PTR_FORMAT, parse->tags);
479   }
480 }
481 
482 static inline gboolean
gst_jpeg_parse_app1(GstJpegParse * parse,GstByteReader * reader)483 gst_jpeg_parse_app1 (GstJpegParse * parse, GstByteReader * reader)
484 {
485   guint16 size = 0;
486   const gchar *id_str;
487   const guint8 *data = NULL;
488 
489   if (!gst_byte_reader_get_uint16_be (reader, &size))
490     return FALSE;
491 
492   size -= 2;                    /* 2 bytes for the mark */
493   if (!gst_byte_reader_peek_string_utf8 (reader, &id_str))
494     return FALSE;
495 
496   if (!strncmp (id_str, "Exif", 4)) {
497 
498     /* skip id + NUL + padding */
499     if (!gst_byte_reader_skip (reader, 6))
500       return FALSE;
501     size -= 6;
502 
503     /* handle exif metadata */
504     if (!gst_byte_reader_get_data (reader, size, &data))
505       return FALSE;
506 
507     extract_and_queue_tags (parse, size, (guint8 *) data,
508         gst_tag_list_from_exif_buffer_with_tiff_header);
509 
510     GST_LOG_OBJECT (parse, "parsed marker %x: '%s' %u bytes",
511         APP1, id_str, size);
512 
513   } else if (!strncmp (id_str, "http://ns.adobe.com/xap/1.0/", 28)) {
514 
515     /* skip the id + NUL */
516     if (!gst_byte_reader_skip (reader, 29))
517       return FALSE;
518     size -= 29;
519 
520     /* handle xmp metadata */
521     if (!gst_byte_reader_get_data (reader, size, &data))
522       return FALSE;
523 
524     extract_and_queue_tags (parse, size, (guint8 *) data,
525         gst_tag_list_from_xmp_buffer);
526 
527     GST_LOG_OBJECT (parse, "parsed marker %x: '%s' %u bytes",
528         APP1, id_str, size);
529 
530   } else {
531     /* restore the byte position and size */
532     reader->size += 2;
533     reader->byte -= 2;
534     if (!gst_jpeg_parse_skip_marker (parse, reader, APP1))
535       return FALSE;
536   }
537 
538   return TRUE;
539 }
540 
541 static inline gchar *
get_utf8_from_data(const guint8 * data,guint16 size)542 get_utf8_from_data (const guint8 * data, guint16 size)
543 {
544   const gchar *env_vars[] = { "GST_JPEG_TAG_ENCODING",
545     "GST_TAG_ENCODING", NULL
546   };
547   const char *str = (gchar *) data;
548 
549   return gst_tag_freeform_string_to_utf8 (str, size, env_vars);
550 }
551 
552 /* read comment and post as tag */
553 static inline gboolean
gst_jpeg_parse_com(GstJpegParse * parse,GstByteReader * reader)554 gst_jpeg_parse_com (GstJpegParse * parse, GstByteReader * reader)
555 {
556   const guint8 *data = NULL;
557   guint16 size = 0;
558   gchar *comment;
559 
560   if (!gst_byte_reader_get_uint16_be (reader, &size))
561     return FALSE;
562 
563   size -= 2;
564   if (!gst_byte_reader_get_data (reader, size, &data))
565     return FALSE;
566 
567   comment = get_utf8_from_data (data, size);
568 
569   if (comment) {
570     GstTagList *taglist = get_tag_list (parse);
571     gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
572         GST_TAG_COMMENT, comment, NULL);
573     GST_DEBUG_OBJECT (parse, "collected tags: %" GST_PTR_FORMAT, taglist);
574     g_free (comment);
575   }
576 
577   return TRUE;
578 }
579 
580 static gboolean
gst_jpeg_parse_read_header(GstJpegParse * parse,GstMapInfo * map,gint len)581 gst_jpeg_parse_read_header (GstJpegParse * parse, GstMapInfo * map, gint len)
582 {
583   GstByteReader reader;
584   guint8 marker = 0;
585   gboolean foundSOF = FALSE;
586 
587   gst_byte_reader_init (&reader, map->data, len);
588 
589   if (!gst_byte_reader_peek_uint8 (&reader, &marker))
590     goto error;
591 
592   while (marker == 0xff) {
593     if (!gst_byte_reader_skip (&reader, 1))
594       goto error;
595 
596     if (!gst_byte_reader_get_uint8 (&reader, &marker))
597       goto error;
598 
599     GST_DEBUG_OBJECT (parse, "marker = %x", marker);
600 
601     switch (marker) {
602       case SOS:                /* start of scan (begins compressed data) */
603         goto done;
604 
605       case SOI:
606         break;
607 
608       case DRI:
609         if (!gst_byte_reader_skip (&reader, 4)) /* fixed size */
610           goto error;
611         break;
612 
613       case COM:
614         if (!gst_jpeg_parse_com (parse, &reader))
615           goto error;
616         break;
617 
618       case APP1:
619         if (!gst_jpeg_parse_app1 (parse, &reader))
620           goto error;
621         break;
622 
623       case DHT:
624       case DQT:
625         /* Ignore these codes */
626         if (!gst_jpeg_parse_skip_marker (parse, &reader, marker))
627           goto error;
628         break;
629 
630       case SOF0:
631         /* parse Start Of Frame */
632         if (!gst_jpeg_parse_sof (parse, &reader))
633           goto error;
634 
635         foundSOF = TRUE;
636         goto done;
637 
638       default:
639         if (marker == JPG || (marker >= JPG0 && marker <= JPG13) ||
640             (marker >= APP0 && marker <= APP15)) {
641           if (!gst_jpeg_parse_skip_marker (parse, &reader, marker))
642             goto error;
643         } else
644           goto unhandled;
645     }
646 
647     if (!gst_byte_reader_peek_uint8 (&reader, &marker))
648       goto error;
649   }
650 done:
651 
652   return foundSOF;
653 
654   /* ERRORS */
655 error:
656   {
657     GST_WARNING_OBJECT (parse,
658         "Error parsing image header (need more than %u bytes available)",
659         gst_byte_reader_get_remaining (&reader));
660     return FALSE;
661   }
662 unhandled:
663   {
664     GST_WARNING_OBJECT (parse, "unhandled marker %x, leaving", marker);
665     /* Not SOF or SOI.  Must not be a JPEG file (or file pointer
666      * is placed wrong).  In either case, it's an error. */
667     return FALSE;
668   }
669 }
670 
671 static gboolean
gst_jpeg_parse_set_new_caps(GstJpegParse * parse,gboolean header_ok)672 gst_jpeg_parse_set_new_caps (GstJpegParse * parse, gboolean header_ok)
673 {
674   GstCaps *caps;
675   gboolean res;
676 
677   GST_DEBUG_OBJECT (parse, "setting caps on srcpad (hdr_ok=%d, have_fps=%d)",
678       header_ok, parse->has_fps);
679 
680   caps = gst_caps_new_simple ("image/jpeg",
681       "parsed", G_TYPE_BOOLEAN, TRUE, NULL);
682 
683   if (header_ok == TRUE) {
684     gst_caps_set_simple (caps,
685         "format", G_TYPE_STRING, parse->format,
686         "width", G_TYPE_INT, parse->width,
687         "height", G_TYPE_INT, parse->height, NULL);
688   }
689 
690   if (parse->has_fps == TRUE) {
691     /* we have a framerate */
692     gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION,
693         parse->framerate_numerator, parse->framerate_denominator, NULL);
694 
695     if (!GST_CLOCK_TIME_IS_VALID (parse->duration)
696         && parse->framerate_numerator != 0) {
697       parse->duration = gst_util_uint64_scale_int (GST_SECOND,
698           parse->framerate_denominator, parse->framerate_numerator);
699     }
700   } else {
701     /* unknown duration */
702     parse->duration = GST_CLOCK_TIME_NONE;
703     gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION, 1, 1, NULL);
704   }
705 
706   GST_DEBUG_OBJECT (parse,
707       "setting downstream caps on %s:%s to %" GST_PTR_FORMAT,
708       GST_DEBUG_PAD_NAME (GST_BASE_PARSE_SRC_PAD (parse)), caps);
709   res = gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps);
710   gst_caps_unref (caps);
711 
712   return res;
713 
714 }
715 
716 static GstFlowReturn
gst_jpeg_parse_pre_push_frame(GstBaseParse * bparse,GstBaseParseFrame * frame)717 gst_jpeg_parse_pre_push_frame (GstBaseParse * bparse, GstBaseParseFrame * frame)
718 {
719   GstJpegParse *parse = GST_JPEG_PARSE_CAST (bparse);
720   GstBuffer *outbuf = frame->buffer;
721 
722   if (parse->has_fps && !GST_CLOCK_TIME_IS_VALID (parse->next_ts))
723     parse->next_ts = bparse->segment.start;
724 
725   GST_BUFFER_TIMESTAMP (outbuf) = parse->next_ts;
726 
727   if (parse->has_fps && GST_CLOCK_TIME_IS_VALID (parse->next_ts)
728       && GST_CLOCK_TIME_IS_VALID (parse->duration)) {
729     parse->next_ts += parse->duration;
730   } else {
731     parse->duration = GST_CLOCK_TIME_NONE;
732     parse->next_ts = GST_CLOCK_TIME_NONE;
733   }
734 
735   GST_BUFFER_DURATION (outbuf) = parse->duration;
736 
737   return GST_FLOW_OK;
738 }
739 
740 static GstFlowReturn
gst_jpeg_parse_handle_frame(GstBaseParse * bparse,GstBaseParseFrame * frame,gint * skipsize)741 gst_jpeg_parse_handle_frame (GstBaseParse * bparse, GstBaseParseFrame * frame,
742     gint * skipsize)
743 {
744   GstJpegParse *parse = GST_JPEG_PARSE_CAST (bparse);
745   gint len;
746   GstClockTime timestamp, duration;
747   GstMapInfo mapinfo;
748   gboolean header_ok;
749 
750   timestamp = GST_BUFFER_PTS (frame->buffer);
751   duration = GST_BUFFER_DURATION (frame->buffer);
752 
753   if (!gst_buffer_map (frame->buffer, &mapinfo, GST_MAP_READ)) {
754     return GST_FLOW_ERROR;
755   }
756 
757   if (!gst_jpeg_parse_skip_to_jpeg_header (parse, &mapinfo, skipsize)) {
758     gst_buffer_unmap (frame->buffer, &mapinfo);
759     return GST_FLOW_OK;
760   }
761 
762   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (parse->next_ts)))
763     parse->next_ts = timestamp;
764 
765   if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (duration)))
766     parse->duration = duration;
767 
768   len = gst_jpeg_parse_get_image_length (parse, &mapinfo);
769   if (len == 0) {
770     gst_buffer_unmap (frame->buffer, &mapinfo);
771     return GST_FLOW_OK;
772   } else if (len < 0) {
773     *skipsize = -len;
774     gst_buffer_unmap (frame->buffer, &mapinfo);
775     return GST_FLOW_OK;
776   }
777 
778   /* check if we already have a EOI */
779   GST_LOG_OBJECT (parse, "parsed image of size %d", len);
780 
781   /* reset the offset (only when we flushed) */
782   parse->last_offset = 0;
783   parse->last_entropy_len = 0;
784 
785   header_ok = gst_jpeg_parse_read_header (parse, &mapinfo, len);
786 
787   gst_buffer_unmap (frame->buffer, &mapinfo);
788 
789   if (parse->width != parse->caps_width
790       || parse->height != parse->caps_height
791       || parse->framerate_numerator !=
792       parse->caps_framerate_numerator
793       || parse->framerate_denominator != parse->caps_framerate_denominator) {
794     if (!gst_jpeg_parse_set_new_caps (parse, header_ok)) {
795       GST_ELEMENT_ERROR (parse, CORE, NEGOTIATION,
796           ("Can't set caps to the src pad"), ("Can't set caps to the src pad"));
797       return GST_FLOW_ERROR;
798     }
799 
800     if (parse->tags) {
801       GST_DEBUG_OBJECT (parse, "Pushing tags: %" GST_PTR_FORMAT, parse->tags);
802       gst_pad_push_event (GST_BASE_PARSE_SRC_PAD (parse),
803           gst_event_new_tag (parse->tags));
804       parse->tags = NULL;
805     }
806 
807     parse->caps_width = parse->width;
808     parse->caps_height = parse->height;
809     parse->caps_framerate_numerator = parse->framerate_numerator;
810     parse->caps_framerate_denominator = parse->framerate_denominator;
811   }
812 
813 
814   return gst_base_parse_finish_frame (bparse, frame, len);
815 }
816 
817 static gboolean
gst_jpeg_parse_sink_event(GstBaseParse * bparse,GstEvent * event)818 gst_jpeg_parse_sink_event (GstBaseParse * bparse, GstEvent * event)
819 {
820   GstJpegParse *parse = GST_JPEG_PARSE_CAST (bparse);
821   gboolean res = TRUE;
822 
823   GST_DEBUG_OBJECT (parse, "event : %s", GST_EVENT_TYPE_NAME (event));
824 
825   switch (GST_EVENT_TYPE (event)) {
826     case GST_EVENT_FLUSH_STOP:
827       parse->next_ts = GST_CLOCK_TIME_NONE;
828       parse->duration = GST_CLOCK_TIME_NONE;
829       parse->last_offset = 0;
830       parse->last_entropy_len = 0;
831       parse->last_resync = FALSE;
832       res = GST_BASE_PARSE_CLASS (parent_class)->sink_event (bparse, event);
833       break;
834     case GST_EVENT_TAG:{
835       if (gst_pad_has_current_caps (GST_BASE_PARSE_SRC_PAD (parse)))
836         res = GST_BASE_PARSE_CLASS (parent_class)->sink_event (bparse, event);
837       else {
838         GstTagList *taglist = NULL;
839 
840         gst_event_parse_tag (event, &taglist);
841         /* Hold on to the tags till the srcpad caps are definitely set */
842         gst_tag_list_insert (get_tag_list (parse), taglist,
843             GST_TAG_MERGE_REPLACE);
844         GST_DEBUG ("collected tags: %" GST_PTR_FORMAT, parse->tags);
845         gst_event_unref (event);
846       }
847       break;
848     }
849     default:
850       res = GST_BASE_PARSE_CLASS (parent_class)->sink_event (bparse, event);
851       break;
852   }
853 
854   return res;
855 }
856 
857 static gboolean
gst_jpeg_parse_start(GstBaseParse * bparse)858 gst_jpeg_parse_start (GstBaseParse * bparse)
859 {
860   GstJpegParse *parse;
861 
862   parse = GST_JPEG_PARSE_CAST (bparse);
863 
864   parse->has_fps = FALSE;
865 
866   parse->width = parse->height = 0;
867   parse->framerate_numerator = 0;
868   parse->framerate_denominator = 1;
869 
870   parse->caps_framerate_numerator = parse->caps_framerate_denominator = 0;
871   parse->caps_width = parse->caps_height = -1;
872 
873   parse->next_ts = GST_CLOCK_TIME_NONE;
874   parse->duration = GST_CLOCK_TIME_NONE;
875 
876   parse->last_offset = 0;
877   parse->last_entropy_len = 0;
878   parse->last_resync = FALSE;
879 
880   parse->tags = NULL;
881 
882   return TRUE;
883 }
884 
885 static gboolean
gst_jpeg_parse_stop(GstBaseParse * bparse)886 gst_jpeg_parse_stop (GstBaseParse * bparse)
887 {
888   GstJpegParse *parse;
889 
890   parse = GST_JPEG_PARSE_CAST (bparse);
891 
892   if (parse->tags) {
893     gst_tag_list_unref (parse->tags);
894     parse->tags = NULL;
895   }
896 
897   return TRUE;
898 }
899