jpegdec: Enable packetized if sink caps contains parsed as true.
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-good / ext / jpeg / gstjpegdec.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) <2009> Tim-Philipp Müller <tim centricular net>
4  * Copyright (C) 2012 Collabora Ltd.
5  *      Author : Edward Hervey <edward@collabora.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 /**
24  * SECTION:element-jpegdec
25  * @title: jpegdec
26  *
27  * Decodes jpeg images.
28  *
29  * ## Example launch line
30  * |[
31  * gst-launch-1.0 -v filesrc location=mjpeg.avi ! avidemux !  queue ! jpegdec ! videoconvert ! videoscale ! autovideosink
32  * ]| The above pipeline decode the mjpeg stream and renders it to the screen.
33  *
34  */
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39 #include <string.h>
40
41 #include "gstjpeg.h"
42 #include "gstjpegdec.h"
43 #include "gstjpegelements.h"
44 #include <gst/video/video.h>
45 #include <gst/video/gstvideometa.h>
46 #include <gst/video/gstvideopool.h>
47 #include <glib/gi18n-lib.h>
48 #include <jerror.h>
49
50 #define MIN_WIDTH  1
51 #define MAX_WIDTH  65535
52 #define MIN_HEIGHT 1
53 #define MAX_HEIGHT 65535
54
55 #define CINFO_GET_JPEGDEC(cinfo_ptr) \
56         (((struct GstJpegDecSourceMgr*)((cinfo_ptr)->src))->dec)
57
58 #define JPEG_DEFAULT_IDCT_METHOD        JDCT_FASTEST
59 #define JPEG_DEFAULT_MAX_ERRORS         0
60
61 enum
62 {
63   PROP_0,
64   PROP_IDCT_METHOD,
65   PROP_MAX_ERRORS
66 };
67
68 /* *INDENT-OFF* */
69 static GstStaticPadTemplate gst_jpeg_dec_src_pad_template =
70 GST_STATIC_PAD_TEMPLATE ("src",
71     GST_PAD_SRC,
72     GST_PAD_ALWAYS,
73     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE
74         ("{ I420, RGB, BGR, RGBx, xRGB, BGRx, xBGR, GRAY8 }"))
75     );
76 /* *INDENT-ON* */
77
78 /* FIXME: sof-marker is for IJG libjpeg 8, should be different for 6.2 */
79 /* FIXME: add back "sof-marker = (int) { 0, 1, 2, 5, 6, 7, 9, 10, 13, 14 }"
80  * once we have a parser and/or demuxer set caps properly */
81 static GstStaticPadTemplate gst_jpeg_dec_sink_pad_template =
82 GST_STATIC_PAD_TEMPLATE ("sink",
83     GST_PAD_SINK,
84     GST_PAD_ALWAYS,
85     GST_STATIC_CAPS ("image/jpeg")
86     );
87
88 GST_DEBUG_CATEGORY_STATIC (jpeg_dec_debug);
89 #define GST_CAT_DEFAULT jpeg_dec_debug
90 GST_DEBUG_CATEGORY_STATIC (GST_CAT_PERFORMANCE);
91
92 static void gst_jpeg_dec_set_property (GObject * object, guint prop_id,
93     const GValue * value, GParamSpec * pspec);
94 static void gst_jpeg_dec_get_property (GObject * object, guint prop_id,
95     GValue * value, GParamSpec * pspec);
96
97 static gboolean gst_jpeg_dec_set_format (GstVideoDecoder * dec,
98     GstVideoCodecState * state);
99 static gboolean gst_jpeg_dec_start (GstVideoDecoder * bdec);
100 static gboolean gst_jpeg_dec_stop (GstVideoDecoder * bdec);
101 static gboolean gst_jpeg_dec_flush (GstVideoDecoder * bdec);
102 static GstFlowReturn gst_jpeg_dec_parse (GstVideoDecoder * bdec,
103     GstVideoCodecFrame * frame, GstAdapter * adapter, gboolean at_eos);
104 static GstFlowReturn gst_jpeg_dec_handle_frame (GstVideoDecoder * bdec,
105     GstVideoCodecFrame * frame);
106 static gboolean gst_jpeg_dec_decide_allocation (GstVideoDecoder * bdec,
107     GstQuery * query);
108 static gboolean gst_jpeg_dec_sink_event (GstVideoDecoder * bdec,
109     GstEvent * event);
110
111 #define gst_jpeg_dec_parent_class parent_class
112 G_DEFINE_TYPE (GstJpegDec, gst_jpeg_dec, GST_TYPE_VIDEO_DECODER);
113 GST_ELEMENT_REGISTER_DEFINE (jpegdec, "jpegdec", GST_RANK_PRIMARY,
114     GST_TYPE_JPEG_DEC);
115
116 static void
117 gst_jpeg_dec_finalize (GObject * object)
118 {
119   GstJpegDec *dec = GST_JPEG_DEC (object);
120
121   jpeg_destroy_decompress (&dec->cinfo);
122   if (dec->input_state)
123     gst_video_codec_state_unref (dec->input_state);
124
125   G_OBJECT_CLASS (parent_class)->finalize (object);
126 }
127
128 static void
129 gst_jpeg_dec_class_init (GstJpegDecClass * klass)
130 {
131   GObjectClass *gobject_class;
132   GstElementClass *element_class;
133   GstVideoDecoderClass *vdec_class;
134
135   gobject_class = (GObjectClass *) klass;
136   element_class = (GstElementClass *) klass;
137   vdec_class = (GstVideoDecoderClass *) klass;
138
139   parent_class = g_type_class_peek_parent (klass);
140
141   gobject_class->finalize = gst_jpeg_dec_finalize;
142   gobject_class->set_property = gst_jpeg_dec_set_property;
143   gobject_class->get_property = gst_jpeg_dec_get_property;
144
145   g_object_class_install_property (gobject_class, PROP_IDCT_METHOD,
146       g_param_spec_enum ("idct-method", "IDCT Method",
147           "The IDCT algorithm to use", GST_TYPE_IDCT_METHOD,
148           JPEG_DEFAULT_IDCT_METHOD,
149           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
150
151   /**
152    * GstJpegDec:max-errors:
153    *
154    * Error out after receiving N consecutive decoding errors
155    * (-1 = never error out, 0 = automatic, 1 = fail on first error, etc.)
156    *
157    * Deprecated: 1.3.1: Property wasn't used internally
158    */
159 #ifndef GST_REMOVE_DEPRECATED
160   g_object_class_install_property (gobject_class, PROP_MAX_ERRORS,
161       g_param_spec_int ("max-errors", "Maximum Consecutive Decoding Errors",
162           "(Deprecated) Error out after receiving N consecutive decoding errors"
163           " (-1 = never fail, 0 = automatic, 1 = fail on first error)",
164           -1, G_MAXINT, JPEG_DEFAULT_MAX_ERRORS,
165           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED));
166 #endif
167
168   gst_element_class_add_static_pad_template (element_class,
169       &gst_jpeg_dec_src_pad_template);
170   gst_element_class_add_static_pad_template (element_class,
171       &gst_jpeg_dec_sink_pad_template);
172   gst_element_class_set_static_metadata (element_class, "JPEG image decoder",
173       "Codec/Decoder/Image", "Decode images from JPEG format",
174       "Wim Taymans <wim@fluendo.com>");
175
176   vdec_class->start = gst_jpeg_dec_start;
177   vdec_class->stop = gst_jpeg_dec_stop;
178   vdec_class->flush = gst_jpeg_dec_flush;
179   vdec_class->parse = gst_jpeg_dec_parse;
180   vdec_class->set_format = gst_jpeg_dec_set_format;
181   vdec_class->handle_frame = gst_jpeg_dec_handle_frame;
182   vdec_class->decide_allocation = gst_jpeg_dec_decide_allocation;
183   vdec_class->sink_event = gst_jpeg_dec_sink_event;
184
185   GST_DEBUG_CATEGORY_INIT (jpeg_dec_debug, "jpegdec", 0, "JPEG decoder");
186   GST_DEBUG_CATEGORY_GET (GST_CAT_PERFORMANCE, "GST_PERFORMANCE");
187
188   gst_type_mark_as_plugin_api (GST_TYPE_IDCT_METHOD, 0);
189 }
190
191 static boolean
192 gst_jpeg_dec_fill_input_buffer (j_decompress_ptr cinfo)
193 {
194   /* We pass in full frame initially, if this get called, the frame is most likely
195    * corrupted */
196   return FALSE;
197 }
198
199 static void
200 gst_jpeg_dec_init_source (j_decompress_ptr cinfo)
201 {
202   GST_LOG_OBJECT (CINFO_GET_JPEGDEC (cinfo), "init_source");
203 }
204
205
206 static void
207 gst_jpeg_dec_skip_input_data (j_decompress_ptr cinfo, glong num_bytes)
208 {
209   GstJpegDec *dec = CINFO_GET_JPEGDEC (cinfo);
210
211   GST_DEBUG_OBJECT (dec, "skip %ld bytes", num_bytes);
212
213   if (num_bytes > 0 && cinfo->src->bytes_in_buffer >= num_bytes) {
214     cinfo->src->next_input_byte += (size_t) num_bytes;
215     cinfo->src->bytes_in_buffer -= (size_t) num_bytes;
216   }
217 }
218
219 static boolean
220 gst_jpeg_dec_resync_to_restart (j_decompress_ptr cinfo, gint desired)
221 {
222   GST_LOG_OBJECT (CINFO_GET_JPEGDEC (cinfo), "resync_to_start");
223   return TRUE;
224 }
225
226 static void
227 gst_jpeg_dec_term_source (j_decompress_ptr cinfo)
228 {
229   GST_LOG_OBJECT (CINFO_GET_JPEGDEC (cinfo), "term_source");
230   return;
231 }
232
233 METHODDEF (void)
234     gst_jpeg_dec_my_output_message (j_common_ptr cinfo)
235 {
236   return;                       /* do nothing */
237 }
238
239 METHODDEF (void)
240     gst_jpeg_dec_my_emit_message (j_common_ptr cinfo, int msg_level)
241 {
242   /* GST_LOG_OBJECT (CINFO_GET_JPEGDEC (&cinfo), "msg_level=%d", msg_level); */
243   return;
244 }
245
246 METHODDEF (void)
247     gst_jpeg_dec_my_error_exit (j_common_ptr cinfo)
248 {
249   struct GstJpegDecErrorMgr *err_mgr = (struct GstJpegDecErrorMgr *) cinfo->err;
250
251   (*cinfo->err->output_message) (cinfo);
252   longjmp (err_mgr->setjmp_buffer, 1);
253 }
254
255 static void
256 gst_jpeg_dec_init (GstJpegDec * dec)
257 {
258   GST_DEBUG ("initializing");
259
260   /* setup jpeglib */
261   memset (&dec->cinfo, 0, sizeof (dec->cinfo));
262   memset (&dec->jerr, 0, sizeof (dec->jerr));
263   dec->cinfo.err = jpeg_std_error (&dec->jerr.pub);
264   dec->jerr.pub.output_message = gst_jpeg_dec_my_output_message;
265   dec->jerr.pub.emit_message = gst_jpeg_dec_my_emit_message;
266   dec->jerr.pub.error_exit = gst_jpeg_dec_my_error_exit;
267
268   jpeg_create_decompress (&dec->cinfo);
269
270   dec->cinfo.src = (struct jpeg_source_mgr *) &dec->jsrc;
271   dec->cinfo.src->init_source = gst_jpeg_dec_init_source;
272   dec->cinfo.src->fill_input_buffer = gst_jpeg_dec_fill_input_buffer;
273   dec->cinfo.src->skip_input_data = gst_jpeg_dec_skip_input_data;
274   dec->cinfo.src->resync_to_restart = gst_jpeg_dec_resync_to_restart;
275   dec->cinfo.src->term_source = gst_jpeg_dec_term_source;
276   dec->jsrc.dec = dec;
277
278   /* init properties */
279   dec->idct_method = JPEG_DEFAULT_IDCT_METHOD;
280   dec->max_errors = JPEG_DEFAULT_MAX_ERRORS;
281
282   gst_video_decoder_set_use_default_pad_acceptcaps (GST_VIDEO_DECODER_CAST
283       (dec), TRUE);
284   GST_PAD_SET_ACCEPT_TEMPLATE (GST_VIDEO_DECODER_SINK_PAD (dec));
285 }
286
287 static inline gboolean
288 gst_jpeg_dec_parse_tag_has_entropy_segment (guint8 tag)
289 {
290   if (tag == 0xda || (tag >= 0xd0 && tag <= 0xd7))
291     return TRUE;
292   return FALSE;
293 }
294
295 static GstFlowReturn
296 gst_jpeg_dec_parse (GstVideoDecoder * bdec, GstVideoCodecFrame * frame,
297     GstAdapter * adapter, gboolean at_eos)
298 {
299   guint size;
300   gint toadd = 0;
301   gboolean resync;
302   gint offset = 0, noffset;
303   GstJpegDec *dec = (GstJpegDec *) bdec;
304
305   GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
306
307   /* FIXME : The overhead of using scan_uint32 is massive */
308
309   size = gst_adapter_available (adapter);
310   GST_DEBUG ("Parsing jpeg image data (%u bytes)", size);
311
312   if (at_eos) {
313     GST_DEBUG ("Flushing all data out");
314     toadd = size;
315
316     /* If we have leftover data, throw it away */
317     if (!dec->saw_header)
318       goto drop_frame;
319     goto have_full_frame;
320   }
321
322   if (size < 8)
323     goto need_more_data;
324
325   if (!dec->saw_header) {
326     gint ret;
327     /* we expect at least 4 bytes, first of which start marker */
328     ret =
329         gst_adapter_masked_scan_uint32 (adapter, 0xffff0000, 0xffd80000, 0,
330         size - 4);
331
332     GST_DEBUG ("ret:%d", ret);
333     if (ret < 0)
334       goto need_more_data;
335
336     if (ret) {
337       gst_adapter_flush (adapter, ret);
338       size -= ret;
339     }
340     dec->saw_header = TRUE;
341   }
342
343   while (1) {
344     guint frame_len;
345     guint32 value;
346
347     GST_DEBUG ("offset:%d, size:%d", offset, size);
348
349     noffset =
350         gst_adapter_masked_scan_uint32_peek (adapter, 0x0000ff00, 0x0000ff00,
351         offset, size - offset, &value);
352
353     /* lost sync if 0xff marker not where expected */
354     if ((resync = (noffset != offset))) {
355       GST_DEBUG ("Lost sync at 0x%08x, resyncing", offset + 2);
356     }
357     /* may have marker, but could have been resyncng */
358     resync = resync || dec->parse_resync;
359     /* Skip over extra 0xff */
360     while ((noffset >= 0) && ((value & 0xff) == 0xff)) {
361       noffset++;
362       noffset =
363           gst_adapter_masked_scan_uint32_peek (adapter, 0x0000ff00, 0x0000ff00,
364           noffset, size - noffset, &value);
365     }
366     /* enough bytes left for marker? (we need 0xNN after the 0xff) */
367     if (noffset < 0) {
368       GST_DEBUG ("at end of input and no EOI marker found, need more data");
369       goto need_more_data;
370     }
371
372     /* now lock on the marker we found */
373     offset = noffset;
374     value = value & 0xff;
375     if (value == 0xd9) {
376       GST_DEBUG ("0x%08x: EOI marker", offset + 2);
377       /* clear parse state */
378       dec->saw_header = FALSE;
379       dec->parse_resync = FALSE;
380       toadd = offset + 4;
381       goto have_full_frame;
382     }
383     if (value == 0xd8) {
384       GST_DEBUG ("0x%08x: SOI marker before EOI marker", offset + 2);
385
386       /* clear parse state */
387       dec->saw_header = FALSE;
388       dec->parse_resync = FALSE;
389       toadd = offset;
390       goto have_full_frame;
391     }
392
393
394     if (value >= 0xd0 && value <= 0xd7)
395       frame_len = 0;
396     else {
397       /* peek tag and subsequent length */
398       if (offset + 2 + 4 > size)
399         goto need_more_data;
400       else
401         gst_adapter_masked_scan_uint32_peek (adapter, 0x0, 0x0, offset + 2, 4,
402             &frame_len);
403       frame_len = frame_len & 0xffff;
404     }
405     GST_DEBUG ("0x%08x: tag %02x, frame_len=%u", offset + 2, value, frame_len);
406     /* the frame length includes the 2 bytes for the length; here we want at
407      * least 2 more bytes at the end for an end marker */
408     if (offset + 2 + 2 + frame_len + 2 > size) {
409       goto need_more_data;
410     }
411
412     if (gst_jpeg_dec_parse_tag_has_entropy_segment (value)) {
413       guint eseglen = dec->parse_entropy_len;
414
415       GST_DEBUG ("0x%08x: finding entropy segment length (eseglen:%d)",
416           offset + 2, eseglen);
417       if (size < offset + 2 + frame_len + eseglen)
418         goto need_more_data;
419       noffset = offset + 2 + frame_len + dec->parse_entropy_len;
420       while (1) {
421         GST_DEBUG ("noffset:%d, size:%d, size - noffset:%d",
422             noffset, size, size - noffset);
423         noffset = gst_adapter_masked_scan_uint32_peek (adapter, 0x0000ff00,
424             0x0000ff00, noffset, size - noffset, &value);
425         if (noffset < 0) {
426           /* need more data */
427           dec->parse_entropy_len = size - offset - 4 - frame_len - 2;
428           goto need_more_data;
429         }
430         if ((value & 0xff) != 0x00) {
431           eseglen = noffset - offset - frame_len - 2;
432           break;
433         }
434         noffset++;
435       }
436       dec->parse_entropy_len = 0;
437       frame_len += eseglen;
438       GST_DEBUG ("entropy segment length=%u => frame_len=%u", eseglen,
439           frame_len);
440     }
441     if (resync) {
442       /* check if we will still be in sync if we interpret
443        * this as a sync point and skip this frame */
444       noffset = offset + frame_len + 2;
445       noffset = gst_adapter_masked_scan_uint32 (adapter, 0x0000ff00, 0x0000ff00,
446           noffset, 4);
447       if (noffset < 0) {
448         /* ignore and continue resyncing until we hit the end
449          * of our data or find a sync point that looks okay */
450         offset++;
451         continue;
452       }
453       GST_DEBUG ("found sync at 0x%x", offset + 2);
454     }
455
456     /* Add current data to output buffer */
457     toadd += frame_len + 2;
458     offset += frame_len + 2;
459   }
460
461 need_more_data:
462   if (toadd)
463     gst_video_decoder_add_to_frame (bdec, toadd);
464   return GST_VIDEO_DECODER_FLOW_NEED_DATA;
465
466 have_full_frame:
467   if (toadd)
468     gst_video_decoder_add_to_frame (bdec, toadd);
469   GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
470   return gst_video_decoder_have_frame (bdec);
471
472 drop_frame:
473   gst_adapter_flush (adapter, size);
474   return GST_FLOW_OK;
475 }
476
477
478 /* shamelessly ripped from jpegutils.c in mjpegtools */
479 static void
480 add_huff_table (j_decompress_ptr dinfo,
481     JHUFF_TBL ** htblptr, const UINT8 * bits, const UINT8 * val)
482 /* Define a Huffman table */
483 {
484   int nsymbols, len;
485
486   if (*htblptr == NULL)
487     *htblptr = jpeg_alloc_huff_table ((j_common_ptr) dinfo);
488
489   g_assert (*htblptr);
490
491   /* Copy the number-of-symbols-of-each-code-length counts */
492   memcpy ((*htblptr)->bits, bits, sizeof ((*htblptr)->bits));
493
494   /* Validate the counts.  We do this here mainly so we can copy the right
495    * number of symbols from the val[] array, without risking marching off
496    * the end of memory.  jchuff.c will do a more thorough test later.
497    */
498   nsymbols = 0;
499   for (len = 1; len <= 16; len++)
500     nsymbols += bits[len];
501   if (nsymbols < 1 || nsymbols > 256)
502     g_error ("jpegutils.c:  add_huff_table failed badly. ");
503
504   memcpy ((*htblptr)->huffval, val, nsymbols * sizeof (UINT8));
505 }
506
507
508
509 static void
510 std_huff_tables (j_decompress_ptr dinfo)
511 /* Set up the standard Huffman tables (cf. JPEG standard section K.3) */
512 /* IMPORTANT: these are only valid for 8-bit data precision! */
513 {
514   static const UINT8 bits_dc_luminance[17] =
515       { /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 };
516   static const UINT8 val_dc_luminance[] =
517       { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
518
519   static const UINT8 bits_dc_chrominance[17] =
520       { /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 };
521   static const UINT8 val_dc_chrominance[] =
522       { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
523
524   static const UINT8 bits_ac_luminance[17] =
525       { /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d };
526   static const UINT8 val_ac_luminance[] =
527       { 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
528     0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
529     0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
530     0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
531     0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
532     0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
533     0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
534     0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
535     0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
536     0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
537     0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
538     0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
539     0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
540     0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
541     0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
542     0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
543     0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
544     0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
545     0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
546     0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
547     0xf9, 0xfa
548   };
549
550   static const UINT8 bits_ac_chrominance[17] =
551       { /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 };
552   static const UINT8 val_ac_chrominance[] =
553       { 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
554     0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
555     0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
556     0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
557     0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
558     0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
559     0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
560     0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
561     0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
562     0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
563     0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
564     0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
565     0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
566     0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
567     0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
568     0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
569     0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
570     0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
571     0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
572     0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
573     0xf9, 0xfa
574   };
575
576   add_huff_table (dinfo, &dinfo->dc_huff_tbl_ptrs[0],
577       bits_dc_luminance, val_dc_luminance);
578   add_huff_table (dinfo, &dinfo->ac_huff_tbl_ptrs[0],
579       bits_ac_luminance, val_ac_luminance);
580   add_huff_table (dinfo, &dinfo->dc_huff_tbl_ptrs[1],
581       bits_dc_chrominance, val_dc_chrominance);
582   add_huff_table (dinfo, &dinfo->ac_huff_tbl_ptrs[1],
583       bits_ac_chrominance, val_ac_chrominance);
584 }
585
586
587
588 static void
589 guarantee_huff_tables (j_decompress_ptr dinfo)
590 {
591   if ((dinfo->dc_huff_tbl_ptrs[0] == NULL) &&
592       (dinfo->dc_huff_tbl_ptrs[1] == NULL) &&
593       (dinfo->ac_huff_tbl_ptrs[0] == NULL) &&
594       (dinfo->ac_huff_tbl_ptrs[1] == NULL)) {
595     GST_DEBUG ("Generating standard Huffman tables for this frame.");
596     std_huff_tables (dinfo);
597   }
598 }
599
600 static gboolean
601 gst_jpeg_dec_set_format (GstVideoDecoder * dec, GstVideoCodecState * state)
602 {
603   GstJpegDec *jpeg = GST_JPEG_DEC (dec);
604   GstStructure *structure;
605   gboolean parsed = FALSE;
606
607   if (jpeg->input_state)
608     gst_video_codec_state_unref (jpeg->input_state);
609   jpeg->input_state = gst_video_codec_state_ref (state);
610
611   structure = gst_caps_get_structure (state->caps, 0);
612   gst_structure_get_boolean (structure, "parsed", &parsed);
613   gst_video_decoder_set_packetized (dec, parsed);
614
615   return TRUE;
616 }
617
618
619 /* yuk */
620 static void
621 hresamplecpy1 (guint8 * dest, const guint8 * src, guint len)
622 {
623   gint i;
624
625   for (i = 0; i < len; ++i) {
626     /* equivalent to: dest[i] = src[i << 1] */
627     *dest = *src;
628     ++dest;
629     ++src;
630     ++src;
631   }
632 }
633
634 static void
635 gst_jpeg_dec_free_buffers (GstJpegDec * dec)
636 {
637   gint i;
638
639   for (i = 0; i < 16; i++) {
640     g_free (dec->idr_y[i]);
641     g_free (dec->idr_u[i]);
642     g_free (dec->idr_v[i]);
643     dec->idr_y[i] = NULL;
644     dec->idr_u[i] = NULL;
645     dec->idr_v[i] = NULL;
646   }
647
648   dec->idr_width_allocated = 0;
649 }
650
651 static inline gboolean
652 gst_jpeg_dec_ensure_buffers (GstJpegDec * dec, guint maxrowbytes)
653 {
654   gint i;
655
656   if (G_LIKELY (dec->idr_width_allocated == maxrowbytes))
657     return TRUE;
658
659   /* FIXME: maybe just alloc one or three blocks altogether? */
660   for (i = 0; i < 16; i++) {
661     dec->idr_y[i] = g_try_realloc (dec->idr_y[i], maxrowbytes);
662     dec->idr_u[i] = g_try_realloc (dec->idr_u[i], maxrowbytes);
663     dec->idr_v[i] = g_try_realloc (dec->idr_v[i], maxrowbytes);
664
665     if (G_UNLIKELY (!dec->idr_y[i] || !dec->idr_u[i] || !dec->idr_v[i])) {
666       GST_WARNING_OBJECT (dec, "out of memory, i=%d, bytes=%u", i, maxrowbytes);
667       return FALSE;
668     }
669   }
670
671   dec->idr_width_allocated = maxrowbytes;
672   GST_LOG_OBJECT (dec, "allocated temp memory, %u bytes/row", maxrowbytes);
673   return TRUE;
674 }
675
676 static void
677 gst_jpeg_dec_decode_grayscale (GstJpegDec * dec, GstVideoFrame * frame,
678     guint field, guint num_fields)
679 {
680   guchar *rows[16];
681   guchar **scanarray[1] = { rows };
682   gint i, j, k;
683   gint lines;
684   guint8 *base[1];
685   gint width, height;
686   gint pstride, rstride;
687
688   GST_DEBUG_OBJECT (dec, "indirect decoding of grayscale");
689
690   width = GST_VIDEO_FRAME_WIDTH (frame);
691   height = GST_VIDEO_FRAME_HEIGHT (frame) / num_fields;
692
693   if (G_UNLIKELY (!gst_jpeg_dec_ensure_buffers (dec, GST_ROUND_UP_32 (width))))
694     return;
695
696   base[0] = GST_VIDEO_FRAME_COMP_DATA (frame, 0);
697   if (field == 2) {
698     base[0] += GST_VIDEO_FRAME_COMP_STRIDE (frame, 0);
699   }
700
701   pstride = GST_VIDEO_FRAME_COMP_PSTRIDE (frame, 0);
702   rstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0) * num_fields;
703
704   memcpy (rows, dec->idr_y, 16 * sizeof (gpointer));
705
706   i = 0;
707   while (i < height) {
708     lines = jpeg_read_raw_data (&dec->cinfo, scanarray, DCTSIZE);
709     if (G_LIKELY (lines > 0)) {
710       for (j = 0; (j < DCTSIZE) && (i < height); j++, i++) {
711         gint p;
712
713         p = 0;
714         for (k = 0; k < width; k++) {
715           base[0][p] = rows[j][k];
716           p += pstride;
717         }
718         base[0] += rstride;
719       }
720     } else {
721       GST_INFO_OBJECT (dec, "jpeg_read_raw_data() returned 0");
722     }
723   }
724 }
725
726 static void
727 gst_jpeg_dec_decode_rgb (GstJpegDec * dec, GstVideoFrame * frame,
728     guint field, guint num_fields)
729 {
730   guchar *r_rows[16], *g_rows[16], *b_rows[16];
731   guchar **scanarray[3] = { r_rows, g_rows, b_rows };
732   gint i, j, k;
733   gint lines;
734   guint8 *base[3];
735   guint pstride, rstride;
736   gint width, height;
737
738   GST_DEBUG_OBJECT (dec, "indirect decoding of RGB");
739
740   width = GST_VIDEO_FRAME_WIDTH (frame);
741   height = GST_VIDEO_FRAME_HEIGHT (frame) / num_fields;
742
743   if (G_UNLIKELY (!gst_jpeg_dec_ensure_buffers (dec, GST_ROUND_UP_32 (width))))
744     return;
745
746   for (i = 0; i < 3; i++) {
747     base[i] = GST_VIDEO_FRAME_COMP_DATA (frame, i);
748     if (field == 2)
749       base[i] += GST_VIDEO_FRAME_COMP_STRIDE (frame, i);
750   }
751
752   pstride = GST_VIDEO_FRAME_COMP_PSTRIDE (frame, 0);
753   rstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0) * num_fields;
754
755   memcpy (r_rows, dec->idr_y, 16 * sizeof (gpointer));
756   memcpy (g_rows, dec->idr_u, 16 * sizeof (gpointer));
757   memcpy (b_rows, dec->idr_v, 16 * sizeof (gpointer));
758
759   i = 0;
760   while (i < height) {
761     lines = jpeg_read_raw_data (&dec->cinfo, scanarray, DCTSIZE);
762     if (G_LIKELY (lines > 0)) {
763       for (j = 0; (j < DCTSIZE) && (i < height); j++, i++) {
764         gint p;
765
766         p = 0;
767         for (k = 0; k < width; k++) {
768           base[0][p] = r_rows[j][k];
769           base[1][p] = g_rows[j][k];
770           base[2][p] = b_rows[j][k];
771           p += pstride;
772         }
773         base[0] += rstride;
774         base[1] += rstride;
775         base[2] += rstride;
776       }
777     } else {
778       GST_INFO_OBJECT (dec, "jpeg_read_raw_data() returned 0");
779     }
780   }
781 }
782
783 static void
784 gst_jpeg_dec_decode_indirect (GstJpegDec * dec, GstVideoFrame * frame, gint r_v,
785     gint r_h, gint comp, guint field, guint num_fields)
786 {
787   guchar *y_rows[16], *u_rows[16], *v_rows[16];
788   guchar **scanarray[3] = { y_rows, u_rows, v_rows };
789   gint i, j, k;
790   gint lines;
791   guchar *base[3], *last[3];
792   gint rowsize[3], stride[3];
793   gint width, height;
794
795   GST_DEBUG_OBJECT (dec,
796       "unadvantageous width or r_h, taking slow route involving memcpy");
797
798   width = GST_VIDEO_FRAME_WIDTH (frame);
799   height = GST_VIDEO_FRAME_HEIGHT (frame);
800
801   if (G_UNLIKELY (!gst_jpeg_dec_ensure_buffers (dec, GST_ROUND_UP_32 (width))))
802     return;
803
804   for (i = 0; i < 3; i++) {
805     base[i] = GST_VIDEO_FRAME_COMP_DATA (frame, i);
806     stride[i] = GST_VIDEO_FRAME_COMP_STRIDE (frame, i) * num_fields;
807     rowsize[i] = GST_VIDEO_FRAME_COMP_STRIDE (frame, i);
808     /* make sure we don't make jpeglib write beyond our buffer,
809      * which might happen if (height % (r_v*DCTSIZE)) != 0 */
810     last[i] = base[i] + (GST_VIDEO_FRAME_COMP_STRIDE (frame, i) *
811         (GST_VIDEO_FRAME_COMP_HEIGHT (frame, i) - 1));
812
813     if (field == 2) {
814       base[i] += GST_VIDEO_FRAME_COMP_STRIDE (frame, i);
815     }
816   }
817
818   memcpy (y_rows, dec->idr_y, 16 * sizeof (gpointer));
819   memcpy (u_rows, dec->idr_u, 16 * sizeof (gpointer));
820   memcpy (v_rows, dec->idr_v, 16 * sizeof (gpointer));
821
822   /* fill chroma components for grayscale */
823   if (comp == 1) {
824     GST_DEBUG_OBJECT (dec, "grayscale, filling chroma");
825     for (i = 0; i < 16; i++) {
826       memset (u_rows[i], GST_ROUND_UP_32 (width), 0x80);
827       memset (v_rows[i], GST_ROUND_UP_32 (width), 0x80);
828     }
829   }
830
831   for (i = 0; i < height; i += r_v * DCTSIZE) {
832     lines = jpeg_read_raw_data (&dec->cinfo, scanarray, r_v * DCTSIZE);
833     if (G_LIKELY (lines > 0)) {
834       for (j = 0, k = 0; j < (r_v * DCTSIZE); j += r_v, k++) {
835         if (G_LIKELY (base[0] <= last[0])) {
836           memcpy (base[0], y_rows[j], rowsize[0]);
837           base[0] += stride[0];
838         }
839         if (r_v == 2) {
840           if (G_LIKELY (base[0] <= last[0])) {
841             memcpy (base[0], y_rows[j + 1], rowsize[0]);
842             base[0] += stride[0];
843           }
844         }
845         if (G_LIKELY (base[1] <= last[1] && base[2] <= last[2])) {
846           if (r_h == 2) {
847             memcpy (base[1], u_rows[k], rowsize[1]);
848             memcpy (base[2], v_rows[k], rowsize[2]);
849           } else if (r_h == 1) {
850             hresamplecpy1 (base[1], u_rows[k], rowsize[1]);
851             hresamplecpy1 (base[2], v_rows[k], rowsize[2]);
852           } else {
853             /* FIXME: implement (at least we avoid crashing by doing nothing) */
854           }
855         }
856
857         if (r_v == 2 || (k & 1) != 0) {
858           base[1] += stride[1];
859           base[2] += stride[2];
860         }
861       }
862     } else {
863       GST_INFO_OBJECT (dec, "jpeg_read_raw_data() returned 0");
864     }
865   }
866 }
867
868 static GstFlowReturn
869 gst_jpeg_dec_decode_direct (GstJpegDec * dec, GstVideoFrame * frame,
870     guint field, guint num_fields)
871 {
872   guchar **line[3];             /* the jpeg line buffer         */
873   guchar *y[4 * DCTSIZE] = { NULL, };   /* alloc enough for the lines   */
874   guchar *u[4 * DCTSIZE] = { NULL, };   /* r_v will be <4               */
875   guchar *v[4 * DCTSIZE] = { NULL, };
876   gint i, j;
877   gint lines, v_samp[3];
878   guchar *base[3], *last[3];
879   gint stride[3];
880   guint height, field_height;
881
882   line[0] = y;
883   line[1] = u;
884   line[2] = v;
885
886   v_samp[0] = dec->cinfo.comp_info[0].v_samp_factor;
887   v_samp[1] = dec->cinfo.comp_info[1].v_samp_factor;
888   v_samp[2] = dec->cinfo.comp_info[2].v_samp_factor;
889
890   if (G_UNLIKELY (v_samp[0] > 2 || v_samp[1] > 2 || v_samp[2] > 2))
891     goto format_not_supported;
892
893   height = field_height = GST_VIDEO_FRAME_HEIGHT (frame);
894
895   /* XXX: division by 2 here might not be a good idea yes. But we are doing this
896    * already in gst_jpeg_dec_handle_frame() for interlaced jpeg */
897   if (num_fields == 2)
898     field_height /= 2;
899
900   for (i = 0; i < 3; i++) {
901     base[i] = GST_VIDEO_FRAME_COMP_DATA (frame, i);
902     stride[i] = GST_VIDEO_FRAME_COMP_STRIDE (frame, i) * num_fields;
903     /* make sure we don't make jpeglib write beyond our buffer,
904      * which might happen if (height % (r_v*DCTSIZE)) != 0 */
905     last[i] = base[i] + (GST_VIDEO_FRAME_COMP_STRIDE (frame, i) *
906         (GST_VIDEO_FRAME_COMP_HEIGHT (frame, i) - 1));
907
908     if (field == 2) {
909       base[i] += GST_VIDEO_FRAME_COMP_STRIDE (frame, i);
910     }
911   }
912
913   if (field_height % (v_samp[0] * DCTSIZE) && (dec->scratch_size < stride[0])) {
914     g_free (dec->scratch);
915     dec->scratch = g_malloc (stride[0]);
916     dec->scratch_size = stride[0];
917   }
918
919   /* let jpeglib decode directly into our final buffer */
920   GST_DEBUG_OBJECT (dec, "decoding directly into output buffer");
921
922 #ifdef JCS_EXTENSIONS
923   if (dec->format_convert) {
924     gint row_stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
925     guchar *bufbase = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
926
927     if (num_fields == 2) {
928       row_stride *= 2;
929     }
930
931     if (field == 2) {
932       bufbase += GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
933     }
934
935     while (dec->cinfo.output_scanline < dec->cinfo.output_height) {
936       JSAMPARRAY buffer = { &bufbase, };
937       jpeg_read_scanlines (&dec->cinfo, buffer, 1);
938       bufbase += row_stride;
939     }
940   } else
941 #endif
942   {
943     for (i = 0; i < height; i += v_samp[0] * DCTSIZE) {
944       for (j = 0; j < (v_samp[0] * DCTSIZE); ++j) {
945         /* Y */
946         line[0][j] = base[0] + (i + j) * stride[0];
947         if (G_UNLIKELY (line[0][j] > last[0]))
948           line[0][j] = dec->scratch;
949         /* U */
950         if (v_samp[1] == v_samp[0]) {
951           line[1][j] = base[1] + ((i + j) / 2) * stride[1];
952         } else if (j < (v_samp[1] * DCTSIZE)) {
953           line[1][j] = base[1] + ((i / 2) + j) * stride[1];
954         }
955         if (G_UNLIKELY (line[1][j] > last[1]))
956           line[1][j] = dec->scratch;
957         /* V */
958         if (v_samp[2] == v_samp[0]) {
959           line[2][j] = base[2] + ((i + j) / 2) * stride[2];
960         } else if (j < (v_samp[2] * DCTSIZE)) {
961           line[2][j] = base[2] + ((i / 2) + j) * stride[2];
962         }
963         if (G_UNLIKELY (line[2][j] > last[2]))
964           line[2][j] = dec->scratch;
965       }
966
967       lines = jpeg_read_raw_data (&dec->cinfo, line, v_samp[0] * DCTSIZE);
968       if (G_UNLIKELY (!lines)) {
969         GST_INFO_OBJECT (dec, "jpeg_read_raw_data() returned 0");
970       }
971     }
972   }
973   return GST_FLOW_OK;
974
975 format_not_supported:
976   {
977     gboolean ret = GST_FLOW_OK;
978
979     GST_VIDEO_DECODER_ERROR (dec, 1, STREAM, DECODE,
980         (_("Failed to decode JPEG image")),
981         ("Unsupported subsampling schema: v_samp factors: %u %u %u", v_samp[0],
982             v_samp[1], v_samp[2]), ret);
983
984     return ret;
985   }
986 }
987
988 #ifdef JCS_EXTENSIONS
989 static J_COLOR_SPACE
990 gst_fmt_to_jpeg_turbo_ext_fmt (GstVideoFormat gstfmt)
991 {
992   switch (gstfmt) {
993     case GST_VIDEO_FORMAT_RGB:
994       return JCS_EXT_RGB;
995     case GST_VIDEO_FORMAT_RGBx:
996       return JCS_EXT_RGBX;
997     case GST_VIDEO_FORMAT_xRGB:
998       return JCS_EXT_XRGB;
999     case GST_VIDEO_FORMAT_RGBA:
1000       return JCS_EXT_RGBA;
1001     case GST_VIDEO_FORMAT_ARGB:
1002       return JCS_EXT_ARGB;
1003     case GST_VIDEO_FORMAT_BGR:
1004       return JCS_EXT_BGR;
1005     case GST_VIDEO_FORMAT_BGRx:
1006       return JCS_EXT_BGRX;
1007     case GST_VIDEO_FORMAT_xBGR:
1008       return JCS_EXT_XBGR;
1009     case GST_VIDEO_FORMAT_BGRA:
1010       return JCS_EXT_BGRA;
1011     case GST_VIDEO_FORMAT_ABGR:
1012       return JCS_EXT_ABGR;
1013     default:
1014       return 0;
1015   }
1016 }
1017
1018 static void
1019 gst_jpeg_turbo_parse_ext_fmt_convert (GstJpegDec * dec, gint * clrspc)
1020 {
1021   GstCaps *peer_caps, *dec_caps;
1022
1023   dec_caps = gst_static_caps_get (&gst_jpeg_dec_src_pad_template.static_caps);
1024   peer_caps =
1025       gst_pad_peer_query_caps (GST_VIDEO_DECODER_SRC_PAD (dec), dec_caps);
1026   gst_caps_unref (dec_caps);
1027
1028   GST_DEBUG ("Received caps from peer: %" GST_PTR_FORMAT, peer_caps);
1029   dec->format_convert = FALSE;
1030   if (!gst_caps_is_empty (peer_caps)) {
1031     GstStructure *peerstruct;
1032     const gchar *peerformat;
1033     GstVideoFormat peerfmt;
1034
1035     if (!gst_caps_is_fixed (peer_caps))
1036       peer_caps = gst_caps_fixate (peer_caps);
1037
1038     peerstruct = gst_caps_get_structure (peer_caps, 0);
1039     peerformat = gst_structure_get_string (peerstruct, "format");
1040     peerfmt = gst_video_format_from_string (peerformat);
1041
1042     switch (peerfmt) {
1043       case GST_VIDEO_FORMAT_RGB:
1044       case GST_VIDEO_FORMAT_RGBx:
1045       case GST_VIDEO_FORMAT_xRGB:
1046       case GST_VIDEO_FORMAT_RGBA:
1047       case GST_VIDEO_FORMAT_ARGB:
1048       case GST_VIDEO_FORMAT_BGR:
1049       case GST_VIDEO_FORMAT_BGRx:
1050       case GST_VIDEO_FORMAT_xBGR:
1051       case GST_VIDEO_FORMAT_BGRA:
1052       case GST_VIDEO_FORMAT_ABGR:
1053         if (clrspc)
1054           *clrspc = JCS_RGB;
1055         dec->format = peerfmt;
1056         dec->format_convert = TRUE;
1057         dec->libjpeg_ext_format = gst_fmt_to_jpeg_turbo_ext_fmt (peerfmt);
1058         break;
1059       default:
1060         break;
1061     }
1062   }
1063   gst_caps_unref (peer_caps);
1064   GST_DEBUG_OBJECT (dec, "format_convert=%d", dec->format_convert);
1065 }
1066 #endif
1067
1068 static void
1069 gst_jpeg_dec_negotiate (GstJpegDec * dec, gint width, gint height, gint clrspc,
1070     gboolean interlaced)
1071 {
1072   GstVideoCodecState *outstate;
1073   GstVideoInfo *info;
1074   GstVideoFormat format;
1075
1076 #ifdef JCS_EXTENSIONS
1077   if (dec->format_convert) {
1078     format = dec->format;
1079   } else
1080 #endif
1081   {
1082     switch (clrspc) {
1083       case JCS_RGB:
1084         format = GST_VIDEO_FORMAT_RGB;
1085         break;
1086       case JCS_GRAYSCALE:
1087         format = GST_VIDEO_FORMAT_GRAY8;
1088         break;
1089       default:
1090         format = GST_VIDEO_FORMAT_I420;
1091         break;
1092     }
1093   }
1094
1095   /* Compare to currently configured output state */
1096   outstate = gst_video_decoder_get_output_state (GST_VIDEO_DECODER (dec));
1097   if (outstate) {
1098     info = &outstate->info;
1099
1100     if (width == GST_VIDEO_INFO_WIDTH (info) &&
1101         height == GST_VIDEO_INFO_HEIGHT (info) &&
1102         format == GST_VIDEO_INFO_FORMAT (info)) {
1103       gst_video_codec_state_unref (outstate);
1104       return;
1105     }
1106     gst_video_codec_state_unref (outstate);
1107   }
1108 #ifdef JCS_EXTENSIONS
1109   /* Determine if libjpeg-turbo direct format conversion can be used
1110    * with current caps and if so, adjust $dec to enable it and $clrspc
1111    * accordingly. */
1112   gst_jpeg_turbo_parse_ext_fmt_convert (dec, &clrspc);
1113 #endif
1114
1115   outstate =
1116       gst_video_decoder_set_output_state (GST_VIDEO_DECODER (dec), format,
1117       width, height, dec->input_state);
1118
1119   switch (clrspc) {
1120     case JCS_RGB:
1121     case JCS_GRAYSCALE:
1122       break;
1123     default:
1124       /* aka JPEG chroma siting */
1125       outstate->info.chroma_site = GST_VIDEO_CHROMA_SITE_NONE;
1126
1127       outstate->info.colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
1128       outstate->info.colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT601;
1129       outstate->info.colorimetry.transfer = GST_VIDEO_TRANSFER_UNKNOWN;
1130       outstate->info.colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_UNKNOWN;
1131       break;
1132   }
1133
1134   if (interlaced) {
1135     outstate->info.interlace_mode = GST_VIDEO_INTERLACE_MODE_INTERLEAVED;
1136     GST_VIDEO_INFO_FIELD_ORDER (&outstate->info) =
1137         GST_VIDEO_FIELD_ORDER_TOP_FIELD_FIRST;
1138   }
1139
1140   gst_video_codec_state_unref (outstate);
1141
1142   gst_video_decoder_negotiate (GST_VIDEO_DECODER (dec));
1143
1144   GST_DEBUG_OBJECT (dec, "max_v_samp_factor=%d", dec->cinfo.max_v_samp_factor);
1145   GST_DEBUG_OBJECT (dec, "max_h_samp_factor=%d", dec->cinfo.max_h_samp_factor);
1146 }
1147
1148 static GstFlowReturn
1149 gst_jpeg_dec_prepare_decode (GstJpegDec * dec)
1150 {
1151   G_GNUC_UNUSED GstFlowReturn ret;
1152   guint r_h, r_v, hdr_ok;
1153
1154   /* read header */
1155   hdr_ok = jpeg_read_header (&dec->cinfo, TRUE);
1156   if (G_UNLIKELY (hdr_ok != JPEG_HEADER_OK)) {
1157     GST_WARNING_OBJECT (dec, "reading the header failed, %d", hdr_ok);
1158   }
1159
1160   GST_LOG_OBJECT (dec, "num_components=%d", dec->cinfo.num_components);
1161   GST_LOG_OBJECT (dec, "jpeg_color_space=%d", dec->cinfo.jpeg_color_space);
1162
1163   if (!dec->cinfo.num_components || !dec->cinfo.comp_info)
1164     goto components_not_supported;
1165
1166   r_h = dec->cinfo.comp_info[0].h_samp_factor;
1167   r_v = dec->cinfo.comp_info[0].v_samp_factor;
1168
1169   GST_LOG_OBJECT (dec, "r_h = %d, r_v = %d", r_h, r_v);
1170
1171   if (dec->cinfo.num_components > 3)
1172     goto components_not_supported;
1173
1174   /* verify color space expectation to avoid going *boom* or bogus output */
1175   if (dec->cinfo.jpeg_color_space != JCS_YCbCr &&
1176       dec->cinfo.jpeg_color_space != JCS_GRAYSCALE &&
1177       dec->cinfo.jpeg_color_space != JCS_RGB)
1178     goto unsupported_colorspace;
1179
1180 #ifndef GST_DISABLE_GST_DEBUG
1181   {
1182     gint i;
1183
1184     for (i = 0; i < dec->cinfo.num_components; ++i) {
1185       GST_LOG_OBJECT (dec, "[%d] h_samp_factor=%d, v_samp_factor=%d, cid=%d",
1186           i, dec->cinfo.comp_info[i].h_samp_factor,
1187           dec->cinfo.comp_info[i].v_samp_factor,
1188           dec->cinfo.comp_info[i].component_id);
1189     }
1190   }
1191 #endif
1192
1193   /* prepare for raw output */
1194   dec->cinfo.do_fancy_upsampling = FALSE;
1195   dec->cinfo.do_block_smoothing = FALSE;
1196   dec->cinfo.dct_method = dec->idct_method;
1197 #ifdef JCS_EXTENSIONS
1198   gst_jpeg_turbo_parse_ext_fmt_convert (dec, NULL);
1199   if (dec->format_convert) {
1200     dec->cinfo.out_color_space = dec->libjpeg_ext_format;
1201     dec->cinfo.raw_data_out = FALSE;
1202   } else
1203 #endif
1204   {
1205     dec->cinfo.out_color_space = dec->cinfo.jpeg_color_space;
1206     dec->cinfo.raw_data_out = TRUE;
1207   }
1208
1209   GST_LOG_OBJECT (dec, "starting decompress");
1210   guarantee_huff_tables (&dec->cinfo);
1211   if (!jpeg_start_decompress (&dec->cinfo)) {
1212     GST_WARNING_OBJECT (dec, "failed to start decompression cycle");
1213   }
1214
1215   /* sanity checks to get safe and reasonable output */
1216   switch (dec->cinfo.jpeg_color_space) {
1217     case JCS_GRAYSCALE:
1218       if (dec->cinfo.num_components != 1)
1219         goto invalid_yuvrgbgrayscale;
1220       break;
1221     case JCS_RGB:
1222       if (dec->cinfo.num_components != 3 || dec->cinfo.max_v_samp_factor > 1 ||
1223           dec->cinfo.max_h_samp_factor > 1)
1224         goto invalid_yuvrgbgrayscale;
1225       break;
1226     case JCS_YCbCr:
1227       if (dec->cinfo.num_components != 3 ||
1228           r_v > 2 || r_v < dec->cinfo.comp_info[0].v_samp_factor ||
1229           r_v < dec->cinfo.comp_info[1].v_samp_factor ||
1230           r_h < dec->cinfo.comp_info[0].h_samp_factor ||
1231           r_h < dec->cinfo.comp_info[1].h_samp_factor)
1232         goto invalid_yuvrgbgrayscale;
1233       break;
1234     default:
1235       g_assert_not_reached ();
1236       break;
1237   }
1238
1239   if (G_UNLIKELY (dec->cinfo.output_width < MIN_WIDTH ||
1240           dec->cinfo.output_width > MAX_WIDTH ||
1241           dec->cinfo.output_height < MIN_HEIGHT ||
1242           dec->cinfo.output_height > MAX_HEIGHT))
1243     goto wrong_size;
1244
1245   return GST_FLOW_OK;
1246
1247 /* ERRORS */
1248 wrong_size:
1249   {
1250     ret = GST_FLOW_ERROR;
1251     GST_VIDEO_DECODER_ERROR (dec, 1, STREAM, DECODE,
1252         (_("Failed to decode JPEG image")),
1253         ("Picture is too small or too big (%ux%u)", dec->cinfo.output_width,
1254             dec->cinfo.output_height), ret);
1255     return GST_FLOW_ERROR;
1256   }
1257 components_not_supported:
1258   {
1259     ret = GST_FLOW_ERROR;
1260     GST_VIDEO_DECODER_ERROR (dec, 1, STREAM, DECODE,
1261         (_("Failed to decode JPEG image")),
1262         ("number of components not supported: %d (max 3)",
1263             dec->cinfo.num_components), ret);
1264     jpeg_abort_decompress (&dec->cinfo);
1265     return GST_FLOW_ERROR;
1266   }
1267 unsupported_colorspace:
1268   {
1269     ret = GST_FLOW_ERROR;
1270     GST_VIDEO_DECODER_ERROR (dec, 1, STREAM, DECODE,
1271         (_("Failed to decode JPEG image")),
1272         ("Picture has unknown or unsupported colourspace"), ret);
1273     jpeg_abort_decompress (&dec->cinfo);
1274     return GST_FLOW_ERROR;
1275   }
1276 invalid_yuvrgbgrayscale:
1277   {
1278     ret = GST_FLOW_ERROR;
1279     GST_VIDEO_DECODER_ERROR (dec, 1, STREAM, DECODE,
1280         (_("Failed to decode JPEG image")),
1281         ("Picture is corrupt or unhandled YUV/RGB/grayscale layout"), ret);
1282     jpeg_abort_decompress (&dec->cinfo);
1283     return GST_FLOW_ERROR;
1284   }
1285 }
1286
1287 static GstFlowReturn
1288 gst_jpeg_dec_decode (GstJpegDec * dec, GstVideoFrame * vframe, guint width,
1289     guint height, guint field, guint num_fields)
1290 {
1291   GstFlowReturn ret = GST_FLOW_OK;
1292
1293   if (dec->cinfo.jpeg_color_space == JCS_RGB) {
1294     gst_jpeg_dec_decode_rgb (dec, vframe, field, num_fields);
1295   } else if (dec->cinfo.jpeg_color_space == JCS_GRAYSCALE) {
1296     gst_jpeg_dec_decode_grayscale (dec, vframe, field, num_fields);
1297   } else {
1298     GST_LOG_OBJECT (dec, "decompressing (required scanline buffer height = %u)",
1299         dec->cinfo.rec_outbuf_height);
1300
1301     /* For some widths jpeglib requires more horizontal padding than I420 
1302      * provides. In those cases we need to decode into separate buffers and then
1303      * copy over the data into our final picture buffer, otherwise jpeglib might
1304      * write over the end of a line into the beginning of the next line,
1305      * resulting in blocky artifacts on the left side of the picture. */
1306     if (G_UNLIKELY (width % (dec->cinfo.max_h_samp_factor * DCTSIZE) != 0
1307             || dec->cinfo.comp_info[0].h_samp_factor != 2
1308             || dec->cinfo.comp_info[1].h_samp_factor != 1
1309             || dec->cinfo.comp_info[2].h_samp_factor != 1)) {
1310       GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, dec,
1311           "indirect decoding using extra buffer copy");
1312       gst_jpeg_dec_decode_indirect (dec, vframe,
1313           dec->cinfo.comp_info[0].v_samp_factor,
1314           dec->cinfo.comp_info[0].h_samp_factor, dec->cinfo.num_components,
1315           field, num_fields);
1316     } else {
1317       ret = gst_jpeg_dec_decode_direct (dec, vframe, field, num_fields);
1318     }
1319   }
1320
1321   GST_LOG_OBJECT (dec, "decompressing finished: %s", gst_flow_get_name (ret));
1322
1323   if (G_UNLIKELY (ret != GST_FLOW_OK)) {
1324     jpeg_abort_decompress (&dec->cinfo);
1325   } else {
1326     jpeg_finish_decompress (&dec->cinfo);
1327   }
1328
1329   return ret;
1330 }
1331
1332 static GstFlowReturn
1333 gst_jpeg_dec_handle_frame (GstVideoDecoder * bdec, GstVideoCodecFrame * frame)
1334 {
1335   GstFlowReturn ret = GST_FLOW_OK;
1336   GstJpegDec *dec = (GstJpegDec *) bdec;
1337   GstVideoFrame vframe;
1338   gint num_fields;              /* number of fields (1 or 2) */
1339   gint output_height;           /* height of output image (one or two fields) */
1340   gint height;                  /* height of current frame (whole image or a field) */
1341   gint width;
1342   guint code;
1343   gboolean need_unmap = TRUE;
1344   GstVideoCodecState *state = NULL;
1345   gboolean release_frame = TRUE;
1346   gboolean has_eoi;
1347   guint8 *data;
1348   gsize nbytes;
1349
1350   if (!gst_buffer_map (frame->input_buffer, &dec->current_frame_map,
1351           GST_MAP_READ))
1352     goto map_failed;
1353
1354   data = dec->current_frame_map.data;
1355   nbytes = dec->current_frame_map.size;
1356   if (nbytes < 2)
1357     goto need_more_data;
1358   has_eoi = ((data[nbytes - 2] == 0xff) && (data[nbytes - 1] == 0xd9));
1359
1360   /* some cameras fail to send an end-of-image marker (EOI),
1361    * add it if that is the case. */
1362   if (!has_eoi) {
1363     GstMapInfo map;
1364     GstBuffer *eoibuf = gst_buffer_new_and_alloc (2);
1365
1366     /* unmap, will add EOI and remap at the end */
1367     gst_buffer_unmap (frame->input_buffer, &dec->current_frame_map);
1368
1369     gst_buffer_map (eoibuf, &map, GST_MAP_WRITE);
1370     map.data[0] = 0xff;
1371     map.data[1] = 0xd9;
1372     gst_buffer_unmap (eoibuf, &map);
1373
1374     /* append to input buffer, and remap */
1375     frame->input_buffer = gst_buffer_append (frame->input_buffer, eoibuf);
1376
1377     gst_buffer_map (frame->input_buffer, &dec->current_frame_map, GST_MAP_READ);
1378     GST_DEBUG ("fixup EOI marker added");
1379   }
1380
1381   dec->current_frame = frame;
1382   dec->cinfo.src->next_input_byte = dec->current_frame_map.data;
1383   dec->cinfo.src->bytes_in_buffer = dec->current_frame_map.size;
1384
1385   if (setjmp (dec->jerr.setjmp_buffer)) {
1386     code = dec->jerr.pub.msg_code;
1387
1388     if (code == JERR_INPUT_EOF) {
1389       GST_DEBUG ("jpeg input EOF error, we probably need more data");
1390       goto need_more_data;
1391     }
1392     goto decode_error;
1393   }
1394
1395   /* read header and check values */
1396   ret = gst_jpeg_dec_prepare_decode (dec);
1397   if (G_UNLIKELY (ret == GST_FLOW_ERROR))
1398     goto done;
1399
1400   width = dec->cinfo.output_width;
1401   height = dec->cinfo.output_height;
1402
1403   /* is it interlaced MJPEG? (we really don't want to scan the jpeg data
1404    * to see if there are two SOF markers in the packet to detect this) */
1405   if (gst_video_decoder_get_packetized (bdec) &&
1406       dec->input_state &&
1407       dec->input_state->info.height > height &&
1408       dec->input_state->info.height <= (height * 2)
1409       && dec->input_state->info.width == width) {
1410     GST_LOG_OBJECT (dec,
1411         "looks like an interlaced image: "
1412         "input width/height of %dx%d with JPEG frame width/height of %dx%d",
1413         dec->input_state->info.width, dec->input_state->info.height, width,
1414         height);
1415     output_height = dec->input_state->info.height;
1416     height = dec->input_state->info.height / 2;
1417     num_fields = 2;
1418     GST_LOG_OBJECT (dec, "field height=%d", height);
1419   } else {
1420     output_height = height;
1421     num_fields = 1;
1422   }
1423
1424   gst_jpeg_dec_negotiate (dec, width, output_height,
1425       dec->cinfo.jpeg_color_space, num_fields == 2);
1426
1427   state = gst_video_decoder_get_output_state (bdec);
1428   ret = gst_video_decoder_allocate_output_frame (bdec, frame);
1429   if (G_UNLIKELY (ret != GST_FLOW_OK))
1430     goto alloc_failed;
1431
1432   if (!gst_video_frame_map (&vframe, &state->info, frame->output_buffer,
1433           GST_MAP_READWRITE))
1434     goto alloc_failed;
1435
1436   if (setjmp (dec->jerr.setjmp_buffer)) {
1437     code = dec->jerr.pub.msg_code;
1438     gst_video_frame_unmap (&vframe);
1439     goto decode_error;
1440   }
1441
1442   GST_LOG_OBJECT (dec, "width %d, height %d, fields %d", width, output_height,
1443       num_fields);
1444
1445   ret = gst_jpeg_dec_decode (dec, &vframe, width, height, 1, num_fields);
1446   if (G_UNLIKELY (ret != GST_FLOW_OK)) {
1447     gst_video_frame_unmap (&vframe);
1448     goto decode_failed;
1449   }
1450
1451   if (setjmp (dec->jerr.setjmp_buffer)) {
1452     code = dec->jerr.pub.msg_code;
1453     gst_video_frame_unmap (&vframe);
1454     goto decode_error;
1455   }
1456
1457   /* decode second field if there is one */
1458   if (num_fields == 2) {
1459     GstVideoFormat field2_format;
1460
1461     /* Checked above before setting num_fields to 2 */
1462     g_assert (dec->input_state != NULL);
1463
1464     /* skip any chunk or padding bytes before the next SOI marker; both fields
1465      * are in one single buffer here, so direct access should be fine here */
1466     while (dec->jsrc.pub.bytes_in_buffer > 2 &&
1467         GST_READ_UINT16_BE (dec->jsrc.pub.next_input_byte) != 0xffd8) {
1468       --dec->jsrc.pub.bytes_in_buffer;
1469       ++dec->jsrc.pub.next_input_byte;
1470     }
1471
1472     if (gst_jpeg_dec_prepare_decode (dec) != GST_FLOW_OK) {
1473       GST_WARNING_OBJECT (dec, "problem reading jpeg header of 2nd field");
1474       /* FIXME: post a warning message here? */
1475       gst_video_frame_unmap (&vframe);
1476       goto decode_failed;
1477     }
1478
1479     /* check if format has changed for the second field */
1480 #ifdef JCS_EXTENSIONS
1481     if (dec->format_convert) {
1482       field2_format = dec->format;
1483     } else
1484 #endif
1485     {
1486       switch (dec->cinfo.jpeg_color_space) {
1487         case JCS_RGB:
1488           field2_format = GST_VIDEO_FORMAT_RGB;
1489           break;
1490         case JCS_GRAYSCALE:
1491           field2_format = GST_VIDEO_FORMAT_GRAY8;
1492           break;
1493         default:
1494           field2_format = GST_VIDEO_FORMAT_I420;
1495           break;
1496       }
1497     }
1498
1499     GST_LOG_OBJECT (dec,
1500         "got for second field of interlaced image: "
1501         "input width/height of %dx%d with JPEG frame width/height of %dx%d",
1502         dec->input_state->info.width, dec->input_state->info.height,
1503         dec->cinfo.output_width, dec->cinfo.output_height);
1504
1505     if (dec->cinfo.output_width != GST_VIDEO_INFO_WIDTH (&state->info) ||
1506         GST_VIDEO_INFO_HEIGHT (&state->info) <= dec->cinfo.output_height ||
1507         GST_VIDEO_INFO_HEIGHT (&state->info) > (dec->cinfo.output_height * 2) ||
1508         field2_format != GST_VIDEO_INFO_FORMAT (&state->info)) {
1509       GST_WARNING_OBJECT (dec, "second field has different format than first");
1510       gst_video_frame_unmap (&vframe);
1511       goto decode_failed;
1512     }
1513
1514     ret = gst_jpeg_dec_decode (dec, &vframe, width, height, 2, 2);
1515     if (G_UNLIKELY (ret != GST_FLOW_OK)) {
1516       gst_video_frame_unmap (&vframe);
1517       goto decode_failed;
1518     }
1519   }
1520   gst_video_frame_unmap (&vframe);
1521
1522   gst_buffer_unmap (frame->input_buffer, &dec->current_frame_map);
1523   ret = gst_video_decoder_finish_frame (bdec, frame);
1524   release_frame = FALSE;
1525   need_unmap = FALSE;
1526
1527 done:
1528
1529 exit:
1530
1531   if (need_unmap)
1532     gst_buffer_unmap (frame->input_buffer, &dec->current_frame_map);
1533
1534   if (release_frame)
1535     gst_video_decoder_release_frame (bdec, frame);
1536
1537   if (state)
1538     gst_video_codec_state_unref (state);
1539
1540   return ret;
1541
1542   /* special cases */
1543 need_more_data:
1544   {
1545     GST_LOG_OBJECT (dec, "we need more data");
1546     ret = GST_FLOW_OK;
1547     goto exit;
1548   }
1549   /* ERRORS */
1550 map_failed:
1551   {
1552     GST_ELEMENT_ERROR (dec, RESOURCE, READ, (_("Failed to read memory")),
1553         ("gst_buffer_map() failed for READ access"));
1554     ret = GST_FLOW_ERROR;
1555     goto exit;
1556   }
1557 decode_error:
1558   {
1559     gchar err_msg[JMSG_LENGTH_MAX];
1560
1561     dec->jerr.pub.format_message ((j_common_ptr) (&dec->cinfo), err_msg);
1562
1563     GST_VIDEO_DECODER_ERROR (dec, 1, STREAM, DECODE,
1564         (_("Failed to decode JPEG image")), ("Decode error #%u: %s", code,
1565             err_msg), ret);
1566
1567     gst_buffer_unmap (frame->input_buffer, &dec->current_frame_map);
1568     gst_video_decoder_drop_frame (bdec, frame);
1569     release_frame = FALSE;
1570     need_unmap = FALSE;
1571     jpeg_abort_decompress (&dec->cinfo);
1572
1573     goto done;
1574   }
1575 decode_failed:
1576   {
1577     /* already posted an error message */
1578     goto done;
1579   }
1580 alloc_failed:
1581   {
1582     const gchar *reason;
1583
1584     reason = gst_flow_get_name (ret);
1585
1586     GST_DEBUG_OBJECT (dec, "failed to alloc buffer, reason %s", reason);
1587     /* Reset for next time */
1588     jpeg_abort_decompress (&dec->cinfo);
1589     if (ret != GST_FLOW_EOS && ret != GST_FLOW_FLUSHING &&
1590         ret != GST_FLOW_NOT_LINKED) {
1591       GST_VIDEO_DECODER_ERROR (dec, 1, STREAM, DECODE,
1592           (_("Failed to decode JPEG image")),
1593           ("Buffer allocation failed, reason: %s", reason), ret);
1594       jpeg_abort_decompress (&dec->cinfo);
1595     }
1596     goto exit;
1597   }
1598 }
1599
1600 static gboolean
1601 gst_jpeg_dec_decide_allocation (GstVideoDecoder * bdec, GstQuery * query)
1602 {
1603   GstBufferPool *pool = NULL;
1604   GstStructure *config;
1605
1606   if (!GST_VIDEO_DECODER_CLASS (parent_class)->decide_allocation (bdec, query))
1607     return FALSE;
1608
1609   if (gst_query_get_n_allocation_pools (query) > 0)
1610     gst_query_parse_nth_allocation_pool (query, 0, &pool, NULL, NULL, NULL);
1611
1612   if (pool == NULL)
1613     return FALSE;
1614
1615   config = gst_buffer_pool_get_config (pool);
1616   if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL)) {
1617     gst_buffer_pool_config_add_option (config,
1618         GST_BUFFER_POOL_OPTION_VIDEO_META);
1619   }
1620   gst_buffer_pool_set_config (pool, config);
1621   gst_object_unref (pool);
1622
1623   return TRUE;
1624 }
1625
1626 static gboolean
1627 gst_jpeg_dec_sink_event (GstVideoDecoder * bdec, GstEvent * event)
1628 {
1629   const GstSegment *segment;
1630
1631   if (GST_EVENT_TYPE (event) != GST_EVENT_SEGMENT)
1632     goto done;
1633
1634   gst_event_parse_segment (event, &segment);
1635
1636   if (segment->format == GST_FORMAT_TIME)
1637     gst_video_decoder_set_packetized (bdec, TRUE);
1638   else
1639     gst_video_decoder_set_packetized (bdec, FALSE);
1640
1641 done:
1642   return GST_VIDEO_DECODER_CLASS (parent_class)->sink_event (bdec, event);
1643 }
1644
1645 static gboolean
1646 gst_jpeg_dec_start (GstVideoDecoder * bdec)
1647 {
1648   GstJpegDec *dec = (GstJpegDec *) bdec;
1649
1650 #ifdef JCS_EXTENSIONS
1651   dec->format_convert = FALSE;
1652 #endif
1653   dec->saw_header = FALSE;
1654   dec->parse_entropy_len = 0;
1655   dec->parse_resync = FALSE;
1656
1657   gst_video_decoder_set_packetized (bdec, FALSE);
1658
1659   return TRUE;
1660 }
1661
1662 static gboolean
1663 gst_jpeg_dec_flush (GstVideoDecoder * bdec)
1664 {
1665   GstJpegDec *dec = (GstJpegDec *) bdec;
1666
1667   jpeg_abort_decompress (&dec->cinfo);
1668   dec->parse_entropy_len = 0;
1669   dec->parse_resync = FALSE;
1670   dec->saw_header = FALSE;
1671 #ifdef JCS_EXTENSIONS
1672   dec->format_convert = FALSE;
1673 #endif
1674
1675   return TRUE;
1676 }
1677
1678 static void
1679 gst_jpeg_dec_set_property (GObject * object, guint prop_id,
1680     const GValue * value, GParamSpec * pspec)
1681 {
1682   GstJpegDec *dec;
1683
1684   dec = GST_JPEG_DEC (object);
1685
1686   switch (prop_id) {
1687     case PROP_IDCT_METHOD:
1688       dec->idct_method = g_value_get_enum (value);
1689       break;
1690 #ifndef GST_REMOVE_DEPRECATED
1691     case PROP_MAX_ERRORS:
1692       g_atomic_int_set (&dec->max_errors, g_value_get_int (value));
1693       break;
1694 #endif
1695     default:
1696       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1697       break;
1698   }
1699 }
1700
1701 static void
1702 gst_jpeg_dec_get_property (GObject * object, guint prop_id, GValue * value,
1703     GParamSpec * pspec)
1704 {
1705   GstJpegDec *dec;
1706
1707   dec = GST_JPEG_DEC (object);
1708
1709   switch (prop_id) {
1710     case PROP_IDCT_METHOD:
1711       g_value_set_enum (value, dec->idct_method);
1712       break;
1713 #ifndef GST_REMOVE_DEPRECATED
1714     case PROP_MAX_ERRORS:
1715       g_value_set_int (value, g_atomic_int_get (&dec->max_errors));
1716       break;
1717 #endif
1718     default:
1719       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1720       break;
1721   }
1722 }
1723
1724 static gboolean
1725 gst_jpeg_dec_stop (GstVideoDecoder * bdec)
1726 {
1727   GstJpegDec *dec = (GstJpegDec *) bdec;
1728
1729   gst_jpeg_dec_free_buffers (dec);
1730
1731   g_free (dec->scratch);
1732   dec->scratch = NULL;
1733   dec->scratch_size = 0;
1734
1735   return TRUE;
1736 }