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