upload tizen1.0 source
[framework/multimedia/gst-plugins-good0.10.git] / gst / rtp / gstrtpjpegpay.c
1 /* GStreamer
2  * Copyright (C) 2008 Axis Communications <dev-gstreamer@axis.com>
3  * @author Bjorn Ostby <bjorn.ostby@axis.com>
4  * @author Peter Kjellerstedt <peter.kjellerstedt@axis.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 /**
23  * SECTION:element-rtpjpegpay
24  *
25  * Payload encode JPEG pictures into RTP packets according to RFC 2435.
26  * For detailed information see: http://www.rfc-editor.org/rfc/rfc2435.txt
27  *
28  * The payloader takes a JPEG picture, scans the header for quantization
29  * tables (if needed) and constructs the RTP packet header followed by
30  * the actual JPEG entropy scan.
31  *
32  * The payloader assumes that correct width and height is found in the caps.
33  */
34
35 #ifdef HAVE_CONFIG_H
36 #  include "config.h"
37 #endif
38
39 #include <string.h>
40 #include <gst/rtp/gstrtpbuffer.h>
41
42 #include "gstrtpjpegpay.h"
43
44 static GstStaticPadTemplate gst_rtp_jpeg_pay_sink_template =
45     GST_STATIC_PAD_TEMPLATE ("sink",
46     GST_PAD_SINK,
47     GST_PAD_ALWAYS,
48     GST_STATIC_CAPS ("image/jpeg; " "video/x-jpeg")
49     );
50
51 static GstStaticPadTemplate gst_rtp_jpeg_pay_src_template =
52 GST_STATIC_PAD_TEMPLATE ("src",
53     GST_PAD_SRC,
54     GST_PAD_ALWAYS,
55     GST_STATIC_CAPS ("application/x-rtp, "
56         "  media = (string) \"video\", "
57         "  payload = (int) 26 ,        "
58         "  clock-rate = (int) 90000,   " "  encoding-name = (string) \"JPEG\"")
59     );
60
61 GST_DEBUG_CATEGORY_STATIC (rtpjpegpay_debug);
62 #define GST_CAT_DEFAULT (rtpjpegpay_debug)
63
64 /*
65  * QUANT_PREFIX_LEN:
66  *
67  * Prefix length in the header before the quantization tables:
68  * Two size bytes and one byte for precision
69  */
70 #define QUANT_PREFIX_LEN     3
71
72 /*
73  * DEFAULT_BUFFER_LIST:
74  *
75  */
76 #define DEFAULT_BUFFER_LIST            FALSE
77
78 typedef enum _RtpJpegMarker RtpJpegMarker;
79
80 /*
81  * RtpJpegMarker:
82  * @JPEG_MARKER: Prefix for JPEG marker
83  * @JPEG_MARKER_SOI: Start of Image marker
84  * @JPEG_MARKER_JFIF: JFIF marker
85  * @JPEG_MARKER_CMT: Comment marker
86  * @JPEG_MARKER_DQT: Define Quantization Table marker
87  * @JPEG_MARKER_SOF: Start of Frame marker
88  * @JPEG_MARKER_DHT: Define Huffman Table marker
89  * @JPEG_MARKER_SOS: Start of Scan marker
90  * @JPEG_MARKER_EOI: End of Image marker
91  * @JPEG_MARKER_DRI: Define Restart Interval marker
92  *
93  * Identifers for markers in JPEG header
94  */
95 enum _RtpJpegMarker
96 {
97   JPEG_MARKER = 0xFF,
98   JPEG_MARKER_SOI = 0xD8,
99   JPEG_MARKER_JFIF = 0xE0,
100   JPEG_MARKER_CMT = 0xFE,
101   JPEG_MARKER_DQT = 0xDB,
102   JPEG_MARKER_SOF = 0xC0,
103   JPEG_MARKER_DHT = 0xC4,
104   JPEG_MARKER_SOS = 0xDA,
105   JPEG_MARKER_EOI = 0xD9,
106   JPEG_MARKER_DRI = 0xDD
107 };
108
109 #define DEFAULT_JPEG_QUANT    255
110
111 #define DEFAULT_JPEG_QUALITY  255
112 #define DEFAULT_JPEG_TYPE     1
113
114 enum
115 {
116   PROP_0,
117   PROP_JPEG_QUALITY,
118   PROP_JPEG_TYPE,
119   PROP_BUFFER_LIST,
120   PROP_LAST
121 };
122
123 enum
124 {
125   Q_TABLE_0 = 0,
126   Q_TABLE_1,
127   Q_TABLE_MAX                   /* only support for two tables at the moment */
128 };
129
130 typedef struct _RtpJpegHeader RtpJpegHeader;
131
132 /*
133  * RtpJpegHeader:
134  * @type_spec: type specific
135  * @offset: fragment offset
136  * @type: type field
137  * @q: quantization table for this frame
138  * @width: width of image in 8-pixel multiples
139  * @height: height of image in 8-pixel multiples
140  *
141  * 0                   1                   2                   3
142  * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
143  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
144  * | Type-specific |              Fragment Offset                  |
145  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
146  * |      Type     |       Q       |     Width     |     Height    |
147  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
148  */
149 struct _RtpJpegHeader
150 {
151   guint type_spec:8;
152   guint offset:24;
153   guint8 type;
154   guint8 q;
155   guint8 width;
156   guint8 height;
157 };
158
159 /*
160  * RtpQuantHeader
161  * @mbz: must be zero
162  * @precision: specify size of quantization tables
163  * @length: length of quantization data
164  *
165  * 0                   1                   2                   3
166  * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
167  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
168  * |      MBZ      |   Precision   |             Length            |
169  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
170  * |                    Quantization Table Data                    |
171  * |                              ...                              |
172  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
173  */
174 typedef struct
175 {
176   guint8 mbz;
177   guint8 precision;
178   guint16 length;
179 } RtpQuantHeader;
180
181 typedef struct
182 {
183   guint8 size;
184   const guint8 *data;
185 } RtpQuantTable;
186
187 /*
188  * RtpRestartMarkerHeader:
189  * @restartInterval: number of MCUs that appear between restart markers
190  * @restartFirstLastCount: a combination of the first packet mark in the chunk
191  *                         last packet mark in the chunk and the position of the
192  *                         first restart interval in the current "chunk"
193  *
194  *    0                   1                   2                   3
195  *   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
196  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
197  *  |       Restart Interval        |F|L|       Restart Count       |
198  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
199  *
200  *  The restart marker header is implemented according to the following
201  *  methodology specified in section 3.1.7 of rfc2435.txt.
202  *
203  *  "If the restart intervals in a frame are not guaranteed to be aligned
204  *  with packet boundaries, the F (first) and L (last) bits MUST be set
205  *  to 1 and the Restart Count MUST be set to 0x3FFF.  This indicates
206  *  that a receiver MUST reassemble the entire frame before decoding it."
207  *
208  */
209
210 typedef struct
211 {
212   guint16 restart_interval;
213   guint16 restart_count;
214 } RtpRestartMarkerHeader;
215
216 typedef struct
217 {
218   guint8 id;
219   guint8 samp;
220   guint8 qt;
221 } CompInfo;
222
223 /* FIXME: restart marker header currently unsupported */
224
225 static void gst_rtp_jpeg_pay_set_property (GObject * object, guint prop_id,
226     const GValue * value, GParamSpec * pspec);
227
228 static void gst_rtp_jpeg_pay_get_property (GObject * object, guint prop_id,
229     GValue * value, GParamSpec * pspec);
230
231 static gboolean gst_rtp_jpeg_pay_setcaps (GstBaseRTPPayload * basepayload,
232     GstCaps * caps);
233
234 static GstFlowReturn gst_rtp_jpeg_pay_handle_buffer (GstBaseRTPPayload * pad,
235     GstBuffer * buffer);
236
237 GST_BOILERPLATE (GstRtpJPEGPay, gst_rtp_jpeg_pay, GstBaseRTPPayload,
238     GST_TYPE_BASE_RTP_PAYLOAD);
239
240 static void
241 gst_rtp_jpeg_pay_base_init (gpointer klass)
242 {
243   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
244
245   gst_element_class_add_pad_template (element_class,
246       gst_static_pad_template_get (&gst_rtp_jpeg_pay_src_template));
247   gst_element_class_add_pad_template (element_class,
248       gst_static_pad_template_get (&gst_rtp_jpeg_pay_sink_template));
249
250   gst_element_class_set_details_simple (element_class, "RTP JPEG payloader",
251       "Codec/Payloader/Network/RTP",
252       "Payload-encodes JPEG pictures into RTP packets (RFC 2435)",
253       "Axis Communications <dev-gstreamer@axis.com>");
254 }
255
256 static void
257 gst_rtp_jpeg_pay_class_init (GstRtpJPEGPayClass * klass)
258 {
259   GObjectClass *gobject_class;
260   GstBaseRTPPayloadClass *gstbasertppayload_class;
261
262   gobject_class = (GObjectClass *) klass;
263   gstbasertppayload_class = (GstBaseRTPPayloadClass *) klass;
264
265   gobject_class->set_property = gst_rtp_jpeg_pay_set_property;
266   gobject_class->get_property = gst_rtp_jpeg_pay_get_property;
267
268   gstbasertppayload_class->set_caps = gst_rtp_jpeg_pay_setcaps;
269   gstbasertppayload_class->handle_buffer = gst_rtp_jpeg_pay_handle_buffer;
270
271   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_JPEG_QUALITY,
272       g_param_spec_int ("quality", "Quality",
273           "Quality factor on JPEG data (unused)", 0, 255, 255,
274           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
275
276   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_JPEG_TYPE,
277       g_param_spec_int ("type", "Type",
278           "Default JPEG Type, overwritten by SOF when present", 0, 255,
279           DEFAULT_JPEG_TYPE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
280
281   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BUFFER_LIST,
282       g_param_spec_boolean ("buffer-list", "Buffer List",
283           "Use Buffer Lists",
284           DEFAULT_BUFFER_LIST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
285
286   GST_DEBUG_CATEGORY_INIT (rtpjpegpay_debug, "rtpjpegpay", 0,
287       "Motion JPEG RTP Payloader");
288 }
289
290 static void
291 gst_rtp_jpeg_pay_init (GstRtpJPEGPay * pay, GstRtpJPEGPayClass * klass)
292 {
293   pay->quality = DEFAULT_JPEG_QUALITY;
294   pay->quant = DEFAULT_JPEG_QUANT;
295   pay->type = DEFAULT_JPEG_TYPE;
296   pay->buffer_list = DEFAULT_BUFFER_LIST;
297 }
298
299 static gboolean
300 gst_rtp_jpeg_pay_setcaps (GstBaseRTPPayload * basepayload, GstCaps * caps)
301 {
302   GstStructure *caps_structure = gst_caps_get_structure (caps, 0);
303   GstRtpJPEGPay *pay;
304   gboolean res;
305   gint width = 0, height = 0;
306
307   pay = GST_RTP_JPEG_PAY (basepayload);
308
309   /* these properties are not mandatory, we can get them from the SOF, if there
310    * is one. */
311   if (gst_structure_get_int (caps_structure, "height", &height)) {
312     if (height <= 0 || height > 2040)
313       goto invalid_dimension;
314   }
315   pay->height = height / 8;
316
317   if (gst_structure_get_int (caps_structure, "width", &width)) {
318     if (width <= 0 || width > 2040)
319       goto invalid_dimension;
320   }
321   pay->width = width / 8;
322
323   gst_basertppayload_set_options (basepayload, "video", TRUE, "JPEG", 90000);
324   res = gst_basertppayload_set_outcaps (basepayload, NULL);
325
326   return res;
327
328   /* ERRORS */
329 invalid_dimension:
330   {
331     GST_ERROR_OBJECT (pay, "Invalid width/height from caps");
332     return FALSE;
333   }
334 }
335
336 static guint
337 gst_rtp_jpeg_pay_header_size (const guint8 * data, guint offset)
338 {
339   return data[offset] << 8 | data[offset + 1];
340 }
341
342 static guint
343 gst_rtp_jpeg_pay_read_quant_table (const guint8 * data, guint size,
344     guint offset, RtpQuantTable tables[])
345 {
346   guint quant_size, tab_size;
347   guint8 prec;
348   guint8 id;
349
350   if (offset + 2 > size)
351     goto too_small;
352
353   quant_size = gst_rtp_jpeg_pay_header_size (data, offset);
354   if (quant_size < 2)
355     goto small_quant_size;
356
357   /* clamp to available data */
358   if (offset + quant_size > size)
359     quant_size = size - offset;
360
361   offset += 2;
362   quant_size -= 2;
363
364   while (quant_size > 0) {
365     /* not enough to read the id */
366     if (offset + 1 > size)
367       break;
368
369     id = data[offset] & 0x0f;
370     if (id == 15)
371       /* invalid id received - corrupt data */
372       goto invalid_id;
373
374     prec = (data[offset] & 0xf0) >> 4;
375     if (prec)
376       tab_size = 128;
377     else
378       tab_size = 64;
379
380     /* there is not enough for the table */
381     if (quant_size < tab_size + 1)
382       goto no_table;
383
384     GST_LOG ("read quant table %d, tab_size %d, prec %02x", id, tab_size, prec);
385
386     tables[id].size = tab_size;
387     tables[id].data = &data[offset + 1];
388
389     tab_size += 1;
390     quant_size -= tab_size;
391     offset += tab_size;
392   }
393 done:
394   return offset + quant_size;
395
396   /* ERRORS */
397 too_small:
398   {
399     GST_WARNING ("not enough data");
400     return size;
401   }
402 small_quant_size:
403   {
404     GST_WARNING ("quant_size too small (%u < 2)", quant_size);
405     return size;
406   }
407 invalid_id:
408   {
409     GST_WARNING ("invalid id");
410     goto done;
411   }
412 no_table:
413   {
414     GST_WARNING ("not enough data for table (%u < %u)", quant_size,
415         tab_size + 1);
416     goto done;
417   }
418 }
419
420 static gboolean
421 gst_rtp_jpeg_pay_read_sof (GstRtpJPEGPay * pay, const guint8 * data,
422     guint size, guint * offset, CompInfo info[])
423 {
424   guint sof_size, off;
425   guint width, height, infolen;
426   CompInfo elem;
427   gint i, j;
428
429   off = *offset;
430
431   /* we need at least 17 bytes for the SOF */
432   if (off + 17 > size)
433     goto wrong_size;
434
435   sof_size = gst_rtp_jpeg_pay_header_size (data, off);
436   if (sof_size < 17)
437     goto wrong_length;
438
439   *offset += sof_size;
440
441   /* skip size */
442   off += 2;
443
444   /* precision should be 8 */
445   if (data[off++] != 8)
446     goto bad_precision;
447
448   /* read dimensions */
449   height = data[off] << 8 | data[off + 1];
450   width = data[off + 2] << 8 | data[off + 3];
451   off += 4;
452
453   GST_LOG_OBJECT (pay, "got dimensions %ux%u", height, width);
454
455   if (height == 0 || height > 2040)
456     goto invalid_dimension;
457   if (width == 0 || width > 2040)
458     goto invalid_dimension;
459
460   pay->height = height / 8;
461   pay->width = width / 8;
462
463   /* we only support 3 components */
464   if (data[off++] != 3)
465     goto bad_components;
466
467   infolen = 0;
468   for (i = 0; i < 3; i++) {
469     elem.id = data[off++];
470     elem.samp = data[off++];
471     elem.qt = data[off++];
472     GST_LOG_OBJECT (pay, "got comp %d, samp %02x, qt %d", elem.id, elem.samp,
473         elem.qt);
474     /* insertion sort from the last element to the first */
475     for (j = infolen; j > 1; j--) {
476       if (G_LIKELY (info[j - 1].id < elem.id))
477         break;
478       info[j] = info[j - 1];
479     }
480     info[j] = elem;
481     infolen++;
482   }
483
484   /* see that the components are supported */
485   if (info[0].samp == 0x21)
486     pay->type = 0;
487   else if (info[0].samp == 0x22)
488     pay->type = 1;
489   else
490     goto invalid_comp;
491
492   if (!(info[1].samp == 0x11))
493     goto invalid_comp;
494
495   if (!(info[2].samp == 0x11))
496     goto invalid_comp;
497
498   /* the other components are free to use any quant table but they have to
499    * have the same table id */
500   if (info[1].qt != info[2].qt)
501     goto invalid_comp;
502
503   return TRUE;
504
505   /* ERRORS */
506 wrong_size:
507   {
508     GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
509         ("Wrong size %u (needed %u).", size, off + 17), (NULL));
510     return FALSE;
511   }
512 wrong_length:
513   {
514     GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
515         ("Wrong SOF length %u.", sof_size), (NULL));
516     return FALSE;
517   }
518 bad_precision:
519   {
520     GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
521         ("Wrong precision, expecting 8."), (NULL));
522     return FALSE;
523   }
524 invalid_dimension:
525   {
526     GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
527         ("Wrong dimension, size %ux%u", width, height), (NULL));
528     return FALSE;
529   }
530 bad_components:
531   {
532     GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
533         ("Wrong number of components"), (NULL));
534     return FALSE;
535   }
536 invalid_comp:
537   {
538     GST_ELEMENT_ERROR (pay, STREAM, FORMAT, ("Invalid component"), (NULL));
539     return FALSE;
540   }
541 }
542
543 static gboolean
544 gst_rtp_jpeg_pay_read_dri (GstRtpJPEGPay * pay, const guint8 * data,
545     guint size, guint * offset, RtpRestartMarkerHeader * dri)
546 {
547   guint dri_size, off;
548
549   off = *offset;
550
551   /* we need at least 4 bytes for the DRI */
552   if (off + 4 > size)
553     goto wrong_size;
554
555   dri_size = gst_rtp_jpeg_pay_header_size (data, off);
556   if (dri_size < 4)
557     goto wrong_length;
558
559   *offset += dri_size;
560   off += 2;
561
562   dri->restart_interval = g_htons ((data[off] << 8) | (data[off + 1]));
563   dri->restart_count = g_htons (0xFFFF);
564
565   return dri->restart_interval > 0;
566
567 wrong_size:
568   {
569     GST_WARNING ("not enough data for DRI");
570     *offset = size;
571     return FALSE;
572   }
573 wrong_length:
574   {
575     GST_WARNING ("DRI size too small (%u)", dri_size);
576     *offset += dri_size;
577     return FALSE;
578   }
579 }
580
581 static RtpJpegMarker
582 gst_rtp_jpeg_pay_scan_marker (const guint8 * data, guint size, guint * offset)
583 {
584   while ((data[(*offset)++] != JPEG_MARKER) && ((*offset) < size));
585
586   if (G_UNLIKELY ((*offset) >= size)) {
587     GST_LOG ("found EOI marker");
588     return JPEG_MARKER_EOI;
589   } else {
590     guint8 marker;
591
592     marker = data[*offset];
593     GST_LOG ("found %02x marker at offset %u", marker, *offset);
594     (*offset)++;
595     return marker;
596   }
597 }
598
599 static GstFlowReturn
600 gst_rtp_jpeg_pay_handle_buffer (GstBaseRTPPayload * basepayload,
601     GstBuffer * buffer)
602 {
603   GstRtpJPEGPay *pay;
604   GstClockTime timestamp;
605   GstFlowReturn ret = GST_FLOW_ERROR;
606   RtpJpegHeader jpeg_header;
607   RtpQuantHeader quant_header;
608   RtpRestartMarkerHeader restart_marker_header;
609   RtpQuantTable tables[15] = { {0, NULL}, };
610   CompInfo info[3] = { {0,}, };
611   guint quant_data_size;
612   guint8 *data;
613   guint size;
614   guint mtu;
615   guint bytes_left;
616   guint jpeg_header_size = 0;
617   guint offset;
618   gboolean frame_done;
619   gboolean sos_found, sof_found, dqt_found, dri_found;
620   gint i;
621   GstBufferList *list = NULL;
622   GstBufferListIterator *it = NULL;
623
624   pay = GST_RTP_JPEG_PAY (basepayload);
625   mtu = GST_BASE_RTP_PAYLOAD_MTU (pay);
626
627   size = GST_BUFFER_SIZE (buffer);
628   data = GST_BUFFER_DATA (buffer);
629   timestamp = GST_BUFFER_TIMESTAMP (buffer);
630   offset = 0;
631
632   GST_LOG_OBJECT (pay, "got buffer size %u, timestamp %" GST_TIME_FORMAT, size,
633       GST_TIME_ARGS (timestamp));
634
635   /* parse the jpeg header for 'start of scan' and read quant tables if needed */
636   sos_found = FALSE;
637   dqt_found = FALSE;
638   sof_found = FALSE;
639   dri_found = FALSE;
640
641   while (!sos_found && (offset < size)) {
642     GST_LOG_OBJECT (pay, "checking from offset %u", offset);
643     switch (gst_rtp_jpeg_pay_scan_marker (data, size, &offset)) {
644       case JPEG_MARKER_JFIF:
645       case JPEG_MARKER_CMT:
646       case JPEG_MARKER_DHT:
647         GST_LOG_OBJECT (pay, "skipping marker");
648         offset += gst_rtp_jpeg_pay_header_size (data, offset);
649         break;
650       case JPEG_MARKER_SOF:
651         if (!gst_rtp_jpeg_pay_read_sof (pay, data, size, &offset, info))
652           goto invalid_format;
653         sof_found = TRUE;
654         break;
655       case JPEG_MARKER_DQT:
656         GST_LOG ("DQT found");
657         offset = gst_rtp_jpeg_pay_read_quant_table (data, size, offset, tables);
658         dqt_found = TRUE;
659         break;
660       case JPEG_MARKER_SOS:
661         sos_found = TRUE;
662         GST_LOG_OBJECT (pay, "SOS found");
663         jpeg_header_size = offset + gst_rtp_jpeg_pay_header_size (data, offset);
664         break;
665       case JPEG_MARKER_EOI:
666         GST_WARNING_OBJECT (pay, "EOI reached before SOS!");
667         break;
668       case JPEG_MARKER_SOI:
669         GST_LOG_OBJECT (pay, "SOI found");
670         break;
671       case JPEG_MARKER_DRI:
672         GST_LOG_OBJECT (pay, "DRI found");
673         if (gst_rtp_jpeg_pay_read_dri (pay, data, size, &offset,
674                 &restart_marker_header))
675           dri_found = TRUE;
676         break;
677       default:
678         break;
679     }
680   }
681   if (!dqt_found || !sof_found)
682     goto unsupported_jpeg;
683
684   /* by now we should either have negotiated the width/height or the SOF header
685    * should have filled us in */
686   if (pay->width == 0 || pay->height == 0)
687     goto no_dimension;
688
689   GST_LOG_OBJECT (pay, "header size %u", jpeg_header_size);
690
691   size -= jpeg_header_size;
692   data += jpeg_header_size;
693   offset = 0;
694
695   if (dri_found)
696     pay->type += 64;
697
698   /* prepare stuff for the jpeg header */
699   jpeg_header.type_spec = 0;
700   jpeg_header.type = pay->type;
701   jpeg_header.q = pay->quant;
702   jpeg_header.width = pay->width;
703   jpeg_header.height = pay->height;
704
705   /* collect the quant headers sizes */
706   quant_header.mbz = 0;
707   quant_header.precision = 0;
708   quant_header.length = 0;
709   quant_data_size = 0;
710
711   if (pay->quant > 127) {
712     /* for the Y and U component, look up the quant table and its size. quant
713      * tables for U and V should be the same */
714     for (i = 0; i < 2; i++) {
715       guint qsize;
716       guint qt;
717
718       qt = info[i].qt;
719       if (qt > 15)
720         goto invalid_quant;
721
722       qsize = tables[qt].size;
723       if (qsize == 0)
724         goto invalid_quant;
725
726       quant_header.precision |= (qsize == 64 ? 0 : (1 << i));
727       quant_data_size += qsize;
728     }
729     quant_header.length = g_htons (quant_data_size);
730     quant_data_size += sizeof (quant_header);
731   }
732
733   GST_LOG_OBJECT (pay, "quant_data size %u", quant_data_size);
734
735   if (pay->buffer_list) {
736     list = gst_buffer_list_new ();
737     it = gst_buffer_list_iterate (list);
738   }
739
740   bytes_left = sizeof (jpeg_header) + quant_data_size + size;
741
742   if (dri_found)
743     bytes_left += sizeof (restart_marker_header);
744
745   frame_done = FALSE;
746   do {
747     GstBuffer *outbuf;
748     guint8 *payload;
749     guint payload_size = (bytes_left < mtu ? bytes_left : mtu);
750
751     if (pay->buffer_list) {
752       guint header_size;
753
754       header_size = sizeof (jpeg_header) + quant_data_size;
755       if (dri_found)
756         header_size += sizeof (restart_marker_header);
757
758       outbuf = gst_rtp_buffer_new_allocate (header_size, 0, 0);
759     } else {
760       outbuf = gst_rtp_buffer_new_allocate (payload_size, 0, 0);
761     }
762     GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
763
764     if (payload_size == bytes_left) {
765       GST_LOG_OBJECT (pay, "last packet of frame");
766       frame_done = TRUE;
767       gst_rtp_buffer_set_marker (outbuf, 1);
768     }
769
770     payload = gst_rtp_buffer_get_payload (outbuf);
771
772     /* update offset */
773 #if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
774     jpeg_header.offset = ((offset & 0x0000FF) << 16) |
775         ((offset & 0xFF0000) >> 16) | (offset & 0x00FF00);
776 #else
777     jpeg_header.offset = offset;
778 #endif
779     memcpy (payload, &jpeg_header, sizeof (jpeg_header));
780     payload += sizeof (jpeg_header);
781     payload_size -= sizeof (jpeg_header);
782
783     if (dri_found) {
784       memcpy (payload, &restart_marker_header, sizeof (restart_marker_header));
785       payload += sizeof (restart_marker_header);
786       payload_size -= sizeof (restart_marker_header);
787     }
788
789     /* only send quant table with first packet */
790     if (G_UNLIKELY (quant_data_size > 0)) {
791       memcpy (payload, &quant_header, sizeof (quant_header));
792       payload += sizeof (quant_header);
793
794       /* copy the quant tables for luma and chrominance */
795       for (i = 0; i < 2; i++) {
796         guint qsize;
797         guint qt;
798
799         qt = info[i].qt;
800         qsize = tables[qt].size;
801         memcpy (payload, tables[qt].data, qsize);
802
803         GST_LOG_OBJECT (pay, "component %d using quant %d, size %d", i, qt,
804             qsize);
805
806         payload += qsize;
807       }
808       payload_size -= quant_data_size;
809       bytes_left -= quant_data_size;
810       quant_data_size = 0;
811     }
812     GST_LOG_OBJECT (pay, "sending payload size %d", payload_size);
813
814     if (pay->buffer_list) {
815       GstBuffer *paybuf;
816
817       /* create a new buf to hold the payload */
818       paybuf = gst_buffer_create_sub (buffer, jpeg_header_size + offset,
819           payload_size);
820
821       /* create a new group to hold the rtp header and the payload */
822       gst_buffer_list_iterator_add_group (it);
823       gst_buffer_list_iterator_add (it, outbuf);
824       gst_buffer_list_iterator_add (it, paybuf);
825     } else {
826       memcpy (payload, data, payload_size);
827       ret = gst_basertppayload_push (basepayload, outbuf);
828       if (ret != GST_FLOW_OK)
829         break;
830     }
831
832     bytes_left -= payload_size;
833     offset += payload_size;
834     data += payload_size;
835   }
836   while (!frame_done);
837
838   if (pay->buffer_list) {
839     gst_buffer_list_iterator_free (it);
840     /* push the whole buffer list at once */
841     ret = gst_basertppayload_push_list (basepayload, list);
842   }
843
844   gst_buffer_unref (buffer);
845
846   return ret;
847
848   /* ERRORS */
849 unsupported_jpeg:
850   {
851     GST_ELEMENT_ERROR (pay, STREAM, FORMAT, ("Unsupported JPEG"), (NULL));
852     gst_buffer_unref (buffer);
853     return GST_FLOW_NOT_SUPPORTED;
854   }
855 no_dimension:
856   {
857     GST_ELEMENT_ERROR (pay, STREAM, FORMAT, ("No size given"), (NULL));
858     gst_buffer_unref (buffer);
859     return GST_FLOW_NOT_NEGOTIATED;
860   }
861 invalid_format:
862   {
863     /* error was posted */
864     gst_buffer_unref (buffer);
865     return GST_FLOW_ERROR;
866   }
867 invalid_quant:
868   {
869     GST_ELEMENT_ERROR (pay, STREAM, FORMAT, ("Invalid quant tables"), (NULL));
870     gst_buffer_unref (buffer);
871     return GST_FLOW_ERROR;
872   }
873 }
874
875 static void
876 gst_rtp_jpeg_pay_set_property (GObject * object, guint prop_id,
877     const GValue * value, GParamSpec * pspec)
878 {
879   GstRtpJPEGPay *rtpjpegpay;
880
881   rtpjpegpay = GST_RTP_JPEG_PAY (object);
882
883   switch (prop_id) {
884     case PROP_JPEG_QUALITY:
885       rtpjpegpay->quality = g_value_get_int (value);
886       GST_DEBUG_OBJECT (object, "quality = %d", rtpjpegpay->quality);
887       break;
888     case PROP_JPEG_TYPE:
889       rtpjpegpay->type = g_value_get_int (value);
890       GST_DEBUG_OBJECT (object, "type = %d", rtpjpegpay->type);
891       break;
892     case PROP_BUFFER_LIST:
893       rtpjpegpay->buffer_list = g_value_get_boolean (value);
894       GST_DEBUG_OBJECT (object, "buffer_list = %d", rtpjpegpay->buffer_list);
895       break;
896     default:
897       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
898       break;
899   }
900 }
901
902 static void
903 gst_rtp_jpeg_pay_get_property (GObject * object, guint prop_id,
904     GValue * value, GParamSpec * pspec)
905 {
906   GstRtpJPEGPay *rtpjpegpay;
907
908   rtpjpegpay = GST_RTP_JPEG_PAY (object);
909
910   switch (prop_id) {
911     case PROP_JPEG_QUALITY:
912       g_value_set_int (value, rtpjpegpay->quality);
913       break;
914     case PROP_JPEG_TYPE:
915       g_value_set_int (value, rtpjpegpay->type);
916       break;
917     case PROP_BUFFER_LIST:
918       g_value_set_boolean (value, rtpjpegpay->buffer_list);
919       break;
920     default:
921       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
922       break;
923   }
924 }
925
926 gboolean
927 gst_rtp_jpeg_pay_plugin_init (GstPlugin * plugin)
928 {
929   return gst_element_register (plugin, "rtpjpegpay", GST_RANK_SECONDARY,
930       GST_TYPE_RTP_JPEG_PAY);
931 }