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