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