gstrtpjpegpay: Added Define Restart Interval (DRI) Marker
[platform/upstream/gstreamer.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 restartInterval;
213   guint16 restartFirstLastCount;
214 } RtpRestartMarkerHeader;
215
216
217 typedef struct
218 {
219   guint8 id;
220   guint8 samp;
221   guint8 qt;
222 } CompInfo;
223
224 /* FIXME: restart marker header currently unsupported */
225
226 static void gst_rtp_jpeg_pay_set_property (GObject * object, guint prop_id,
227     const GValue * value, GParamSpec * pspec);
228
229 static void gst_rtp_jpeg_pay_get_property (GObject * object, guint prop_id,
230     GValue * value, GParamSpec * pspec);
231
232 static gboolean gst_rtp_jpeg_pay_setcaps (GstBaseRTPPayload * basepayload,
233     GstCaps * caps);
234
235 static GstFlowReturn gst_rtp_jpeg_pay_handle_buffer (GstBaseRTPPayload * pad,
236     GstBuffer * buffer);
237
238 GST_BOILERPLATE (GstRtpJPEGPay, gst_rtp_jpeg_pay, GstBaseRTPPayload,
239     GST_TYPE_BASE_RTP_PAYLOAD);
240
241 static void
242 gst_rtp_jpeg_pay_base_init (gpointer klass)
243 {
244   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
245
246   gst_element_class_add_pad_template (element_class,
247       gst_static_pad_template_get (&gst_rtp_jpeg_pay_src_template));
248   gst_element_class_add_pad_template (element_class,
249       gst_static_pad_template_get (&gst_rtp_jpeg_pay_sink_template));
250
251   gst_element_class_set_details_simple (element_class, "RTP JPEG payloader",
252       "Codec/Payloader/Network",
253       "Payload-encodes JPEG pictures into RTP packets (RFC 2435)",
254       "Axis Communications <dev-gstreamer@axis.com>");
255 }
256
257 static void
258 gst_rtp_jpeg_pay_class_init (GstRtpJPEGPayClass * klass)
259 {
260   GObjectClass *gobject_class;
261   GstBaseRTPPayloadClass *gstbasertppayload_class;
262
263   gobject_class = (GObjectClass *) klass;
264   gstbasertppayload_class = (GstBaseRTPPayloadClass *) klass;
265
266   gobject_class->set_property = gst_rtp_jpeg_pay_set_property;
267   gobject_class->get_property = gst_rtp_jpeg_pay_get_property;
268
269   gstbasertppayload_class->set_caps = gst_rtp_jpeg_pay_setcaps;
270   gstbasertppayload_class->handle_buffer = gst_rtp_jpeg_pay_handle_buffer;
271
272   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_JPEG_QUALITY,
273       g_param_spec_int ("quality", "Quality",
274           "Quality factor on JPEG data (unused)", 0, 255, 255,
275           G_PARAM_READWRITE));
276
277   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_JPEG_TYPE,
278       g_param_spec_int ("type", "Type",
279           "Default JPEG Type, overwritten by SOF when present", 0, 255,
280           DEFAULT_JPEG_TYPE, G_PARAM_READWRITE));
281
282   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BUFFER_LIST,
283       g_param_spec_boolean ("buffer-list", "Buffer List",
284           "Use Buffer Lists",
285           DEFAULT_BUFFER_LIST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
286
287   GST_DEBUG_CATEGORY_INIT (rtpjpegpay_debug, "rtpjpegpay", 0,
288       "Motion JPEG RTP Payloader");
289 }
290
291 static void
292 gst_rtp_jpeg_pay_init (GstRtpJPEGPay * pay, GstRtpJPEGPayClass * klass)
293 {
294   pay->quality = DEFAULT_JPEG_QUALITY;
295   pay->quant = DEFAULT_JPEG_QUANT;
296   pay->type = DEFAULT_JPEG_TYPE;
297   pay->buffer_list = DEFAULT_BUFFER_LIST;
298 }
299
300 static gboolean
301 gst_rtp_jpeg_pay_setcaps (GstBaseRTPPayload * basepayload, GstCaps * caps)
302 {
303   GstStructure *caps_structure = gst_caps_get_structure (caps, 0);
304   GstRtpJPEGPay *pay;
305   gboolean res;
306   gint width = 0, height = 0;
307
308   pay = GST_RTP_JPEG_PAY (basepayload);
309
310   /* these properties are not mandatory, we can get them from the SOF, if there
311    * is one. */
312   if (gst_structure_get_int (caps_structure, "height", &height)) {
313     if (height <= 0 || height > 2040)
314       goto invalid_dimension;
315   }
316   pay->height = height / 8;
317
318   if (gst_structure_get_int (caps_structure, "width", &width)) {
319     if (width <= 0 || width > 2040)
320       goto invalid_dimension;
321   }
322   pay->width = width / 8;
323
324   gst_basertppayload_set_options (basepayload, "video", TRUE, "JPEG", 90000);
325   res = gst_basertppayload_set_outcaps (basepayload, NULL);
326
327   return res;
328
329   /* ERRORS */
330 invalid_dimension:
331   {
332     GST_ERROR_OBJECT (pay, "Invalid width/height from caps");
333     return FALSE;
334   }
335 }
336
337 static guint
338 gst_rtp_jpeg_pay_header_size (const guint8 * data, guint offset)
339 {
340   return data[offset] << 8 | data[offset + 1];
341 }
342
343 static guint
344 gst_rtp_jpeg_pay_read_quant_table (const guint8 * data, guint size,
345     guint offset, RtpQuantTable tables[])
346 {
347   guint quant_size, tab_size;
348   guint8 prec;
349   guint8 id;
350
351   if (offset + 2 > size)
352     goto too_small;
353
354   quant_size = gst_rtp_jpeg_pay_header_size (data, offset);
355   if (quant_size < 2)
356     goto small_quant_size;
357
358   /* clamp to available data */
359   if (offset + quant_size > size)
360     quant_size = size - offset;
361
362   offset += 2;
363   quant_size -= 2;
364
365   while (quant_size > 0) {
366     /* not enough to read the id */
367     if (offset + 1 > size)
368       break;
369
370     id = data[offset] & 0x0f;
371     if (id == 15)
372       /* invalid id received - corrupt data */
373       goto invalid_id;
374
375     prec = (data[offset] & 0xf0) >> 4;
376     if (prec)
377       tab_size = 128;
378     else
379       tab_size = 64;
380
381     /* there is not enough for the table */
382     if (quant_size < tab_size + 1)
383       goto no_table;
384
385     GST_LOG ("read quant table %d, tab_size %d, prec %02x", id, tab_size, prec);
386
387     tables[id].size = tab_size;
388     tables[id].data = &data[offset + 1];
389
390     tab_size += 1;
391     quant_size -= tab_size;
392     offset += tab_size;
393   }
394 done:
395   return offset + quant_size;
396
397   /* ERRORS */
398 too_small:
399   {
400     GST_WARNING ("not enough data");
401     return size;
402   }
403 small_quant_size:
404   {
405     GST_WARNING ("quant_size too small (%u < 2)", quant_size);
406     return size;
407   }
408 invalid_id:
409   {
410     GST_WARNING ("invalid id");
411     goto done;
412   }
413 no_table:
414   {
415     GST_WARNING ("not enough data for table (%u < %u)", quant_size,
416         tab_size + 1);
417     goto done;
418   }
419 }
420
421 static gboolean
422 gst_rtp_jpeg_pay_read_sof (GstRtpJPEGPay * pay, const guint8 * data,
423     guint size, guint * offset, CompInfo info[])
424 {
425   guint sof_size, off;
426   guint width, height, infolen;
427   CompInfo elem;
428   gint i, j;
429
430   off = *offset;
431
432   /* we need at least 17 bytes for the SOF */
433   if (off + 17 > size)
434     goto wrong_size;
435
436   sof_size = gst_rtp_jpeg_pay_header_size (data, off);
437   if (sof_size < 17)
438     goto wrong_length;
439
440   *offset += sof_size;
441
442   /* skip size */
443   off += 2;
444
445   /* precision should be 8 */
446   if (data[off++] != 8)
447     goto bad_precision;
448
449   /* read dimensions */
450   height = data[off] << 8 | data[off + 1];
451   width = data[off + 2] << 8 | data[off + 3];
452   off += 4;
453
454   GST_LOG_OBJECT (pay, "got dimensions %ux%u", height, width);
455
456   if (height == 0 || height > 2040)
457     goto invalid_dimension;
458   if (width == 0 || width > 2040)
459     goto invalid_dimension;
460
461   pay->height = height / 8;
462   pay->width = width / 8;
463
464   /* we only support 3 components */
465   if (data[off++] != 3)
466     goto bad_components;
467
468   infolen = 0;
469   for (i = 0; i < 3; i++) {
470     elem.id = data[off++];
471     elem.samp = data[off++];
472     elem.qt = data[off++];
473     GST_LOG_OBJECT (pay, "got comp %d, samp %02x, qt %d", elem.id, elem.samp,
474         elem.qt);
475     /* insertion sort from the last element to the first */
476     for (j = infolen; j > 1; j--) {
477       if (G_LIKELY (info[j - 1].id < elem.id))
478         break;
479       info[j] = info[j - 1];
480     }
481     info[j] = elem;
482     infolen++;
483   }
484
485   /* see that the components are supported */
486   if (info[0].samp == 0x21)
487     pay->type = 0;
488   else if (info[0].samp == 0x22)
489     pay->type = 1;
490   else
491     goto invalid_comp;
492
493   if (!(info[1].samp == 0x11))
494     goto invalid_comp;
495
496   if (!(info[2].samp == 0x11))
497     goto invalid_comp;
498
499   /* the other components are free to use any quant table but they have to
500    * have the same table id */
501   if (info[1].qt != info[2].qt)
502     goto invalid_comp;
503
504   return TRUE;
505
506   /* ERRORS */
507 wrong_size:
508   {
509     GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
510         ("Wrong size %u (needed %u).", size, off + 17), (NULL));
511     return FALSE;
512   }
513 wrong_length:
514   {
515     GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
516         ("Wrong SOF length %u.", sof_size), (NULL));
517     return FALSE;
518   }
519 bad_precision:
520   {
521     GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
522         ("Wrong precision, expecting 8."), (NULL));
523     return FALSE;
524   }
525 invalid_dimension:
526   {
527     GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
528         ("Wrong dimension, size %ux%u", width, height), (NULL));
529     return FALSE;
530   }
531 bad_components:
532   {
533     GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
534         ("Wrong number of components"), (NULL));
535     return FALSE;
536   }
537 invalid_comp:
538   {
539     GST_ELEMENT_ERROR (pay, STREAM, FORMAT, ("Invalid component"), (NULL));
540     return FALSE;
541   }
542 }
543
544 static RtpJpegMarker
545 gst_rtp_jpeg_pay_scan_marker (const guint8 * data, guint size, guint * offset)
546 {
547   while ((data[(*offset)++] != JPEG_MARKER) && ((*offset) < size));
548
549   if (G_UNLIKELY ((*offset) >= size)) {
550     GST_LOG ("found EOI marker");
551     return JPEG_MARKER_EOI;
552   } else {
553     guint8 marker;
554
555     marker = data[*offset];
556     GST_LOG ("found %02x marker at offset %u", marker, *offset);
557     (*offset)++;
558     return marker;
559   }
560 }
561
562 static GstFlowReturn
563 gst_rtp_jpeg_pay_handle_buffer (GstBaseRTPPayload * basepayload,
564     GstBuffer * buffer)
565 {
566   GstRtpJPEGPay *pay;
567   GstClockTime timestamp;
568   GstFlowReturn ret = GST_FLOW_ERROR;
569   RtpJpegHeader jpeg_header;
570   RtpQuantHeader quant_header;
571   RtpRestartMarkerHeader restart_marker_header;
572   RtpQuantTable tables[15] = { {0, NULL}, };
573   CompInfo info[3] = { {0,}, };
574   guint quant_data_size;
575   guint8 *data;
576   guint size;
577   guint mtu;
578   guint bytes_left;
579   guint jpeg_header_size = 0;
580   guint offset;
581   gboolean frame_done;
582   gboolean sos_found, sof_found, dqt_found, dri_found;
583   gint i;
584   GstBufferList *list = NULL;
585   GstBufferListIterator *it = NULL;
586
587   pay = GST_RTP_JPEG_PAY (basepayload);
588   mtu = GST_BASE_RTP_PAYLOAD_MTU (pay);
589
590   size = GST_BUFFER_SIZE (buffer);
591   data = GST_BUFFER_DATA (buffer);
592   timestamp = GST_BUFFER_TIMESTAMP (buffer);
593   offset = 0;
594
595   GST_LOG_OBJECT (pay, "got buffer size %u, timestamp %" GST_TIME_FORMAT, size,
596       GST_TIME_ARGS (timestamp));
597
598   /* parse the jpeg header for 'start of scan' and read quant tables if needed */
599   sos_found = FALSE;
600   dqt_found = FALSE;
601   sof_found = FALSE;
602   dri_found = FALSE;
603
604   while (!sos_found && (offset < size)) {
605     GST_LOG_OBJECT (pay, "checking from offset %u", offset);
606     switch (gst_rtp_jpeg_pay_scan_marker (data, size, &offset)) {
607       case JPEG_MARKER_JFIF:
608       case JPEG_MARKER_CMT:
609       case JPEG_MARKER_DHT:
610         GST_LOG_OBJECT (pay, "skipping marker");
611         offset += gst_rtp_jpeg_pay_header_size (data, offset);
612         break;
613       case JPEG_MARKER_SOF:
614         if (!gst_rtp_jpeg_pay_read_sof (pay, data, size, &offset, info))
615           goto invalid_format;
616         sof_found = TRUE;
617         break;
618       case JPEG_MARKER_DQT:
619         GST_LOG ("DQT found");
620         offset = gst_rtp_jpeg_pay_read_quant_table (data, size, offset, tables);
621         dqt_found = TRUE;
622         break;
623       case JPEG_MARKER_SOS:
624         sos_found = TRUE;
625         GST_LOG_OBJECT (pay, "SOS found");
626         jpeg_header_size = offset + gst_rtp_jpeg_pay_header_size (data, offset);
627         break;
628       case JPEG_MARKER_EOI:
629         GST_WARNING_OBJECT (pay, "EOI reached before SOS!");
630         break;
631       case JPEG_MARKER_SOI:
632         GST_LOG_OBJECT (pay, "SOI found");
633         break;
634       case JPEG_MARKER_DRI:
635         GST_LOG_OBJECT (pay, "DRI found");
636         restart_marker_header.restartInterval=g_htons((data[offset+2] << 8) | (data[offset + 3]));
637         restart_marker_header.restartFirstLastCount=g_htons(0xFFFF);
638         if (restart_marker_header.restartInterval > 0) {
639           dri_found = TRUE;
640         }
641                 break;      
642       default:
643         break;
644     }
645   }
646   if (!dqt_found || !sof_found)
647     goto unsupported_jpeg;
648
649   /* by now we should either have negotiated the width/height or the SOF header
650    * should have filled us in */
651   if (pay->width == 0 || pay->height == 0)
652     goto no_dimension;
653
654   GST_LOG_OBJECT (pay, "header size %u", jpeg_header_size);
655
656   size -= jpeg_header_size;
657   data += jpeg_header_size;
658   offset = 0;
659
660   if (dri_found)
661     pay->type += 64;
662
663   /* prepare stuff for the jpeg header */
664   jpeg_header.type_spec = 0;
665   jpeg_header.type = pay->type;
666   jpeg_header.q = pay->quant;
667   jpeg_header.width = pay->width;
668   jpeg_header.height = pay->height;
669
670   /* collect the quant headers sizes */
671   quant_header.mbz = 0;
672   quant_header.precision = 0;
673   quant_header.length = 0;
674   quant_data_size = 0;
675
676   if (pay->quant > 127) {
677     /* for the Y and U component, look up the quant table and its size. quant
678      * tables for U and V should be the same */
679     for (i = 0; i < 2; i++) {
680       guint qsize;
681       guint qt;
682
683       qt = info[i].qt;
684       if (qt > 15)
685         goto invalid_quant;
686
687       qsize = tables[qt].size;
688       if (qsize == 0)
689         goto invalid_quant;
690
691       quant_header.precision |= (qsize == 64 ? 0 : (1 << i));
692       quant_data_size += qsize;
693     }
694     quant_header.length = g_htons (quant_data_size);
695     quant_data_size += sizeof (quant_header);
696   }
697
698   GST_LOG_OBJECT (pay, "quant_data size %u", quant_data_size);
699
700   if (pay->buffer_list) {
701     list = gst_buffer_list_new ();
702     it = gst_buffer_list_iterate (list);
703   }
704
705   bytes_left = sizeof (jpeg_header) + quant_data_size + size;
706
707   if (dri_found)
708     bytes_left += sizeof (restart_marker_header);
709
710   frame_done = FALSE;
711   do {
712     GstBuffer *outbuf;
713     guint8 *payload;
714     guint payload_size = (bytes_left < mtu ? bytes_left : mtu);
715
716     if (pay->buffer_list) {
717           if (dri_found){
718         outbuf = gst_rtp_buffer_new_allocate (sizeof (jpeg_header) +
719           sizeof (restart_marker_header) + quant_data_size, 0, 0);
720           } else {
721         outbuf = gst_rtp_buffer_new_allocate (sizeof (jpeg_header) +
722           quant_data_size, 0, 0);
723           }
724     } else {
725       outbuf = gst_rtp_buffer_new_allocate (payload_size, 0, 0);
726     }
727     GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
728
729     if (payload_size == bytes_left) {
730       GST_LOG_OBJECT (pay, "last packet of frame");
731       frame_done = TRUE;
732       gst_rtp_buffer_set_marker (outbuf, 1);
733     }
734
735     payload = gst_rtp_buffer_get_payload (outbuf);
736
737     /* update offset */
738 #if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
739     jpeg_header.offset = ((offset & 0x0000FF) << 16) |
740         ((offset & 0xFF0000) >> 16) | (offset & 0x00FF00);
741 #else
742     jpeg_header.offset = offset;
743 #endif
744     memcpy (payload, &jpeg_header, sizeof (jpeg_header));
745     payload += sizeof (jpeg_header);
746     payload_size -= sizeof (jpeg_header);
747
748     if (dri_found)
749     {
750       memcpy (payload, &restart_marker_header, sizeof (restart_marker_header));
751       payload += sizeof (restart_marker_header);
752       payload_size -= sizeof (restart_marker_header);       
753     }
754
755     /* only send quant table with first packet */
756     if (G_UNLIKELY (quant_data_size > 0)) {
757       memcpy (payload, &quant_header, sizeof (quant_header));
758       payload += sizeof (quant_header);
759
760       /* copy the quant tables for luma and chrominance */
761       for (i = 0; i < 2; i++) {
762         guint qsize;
763         guint qt;
764
765         qt = info[i].qt;
766         qsize = tables[qt].size;
767         memcpy (payload, tables[qt].data, qsize);
768
769         GST_LOG_OBJECT (pay, "component %d using quant %d, size %d", i, qt,
770             qsize);
771
772         payload += qsize;
773       }
774       payload_size -= quant_data_size;
775       bytes_left -= quant_data_size;
776       quant_data_size = 0;
777     }
778     GST_LOG_OBJECT (pay, "sending payload size %d", payload_size);
779
780     if (pay->buffer_list) {
781       GstBuffer *paybuf;
782
783       /* create a new buf to hold the payload */
784       paybuf = gst_buffer_create_sub (buffer, jpeg_header_size + offset,
785           payload_size);
786
787       /* create a new group to hold the rtp header and the payload */
788       gst_buffer_list_iterator_add_group (it);
789       gst_buffer_list_iterator_add (it, outbuf);
790       gst_buffer_list_iterator_add (it, paybuf);
791     } else {
792       memcpy (payload, data, payload_size);
793       ret = gst_basertppayload_push (basepayload, outbuf);
794       if (ret != GST_FLOW_OK)
795         break;
796     }
797
798     bytes_left -= payload_size;
799     offset += payload_size;
800     data += payload_size;
801   }
802   while (!frame_done);
803
804   if (pay->buffer_list) {
805     gst_buffer_list_iterator_free (it);
806     /* push the whole buffer list at once */
807     ret = gst_basertppayload_push_list (basepayload, list);
808   }
809
810   gst_buffer_unref (buffer);
811
812   return ret;
813
814   /* ERRORS */
815 unsupported_jpeg:
816   {
817     GST_ELEMENT_ERROR (pay, STREAM, FORMAT, ("Unsupported JPEG"), (NULL));
818     gst_buffer_unref (buffer);
819     return GST_FLOW_NOT_SUPPORTED;
820   }
821 no_dimension:
822   {
823     GST_ELEMENT_ERROR (pay, STREAM, FORMAT, ("No size given"), (NULL));
824     gst_buffer_unref (buffer);
825     return GST_FLOW_NOT_NEGOTIATED;
826   }
827 invalid_format:
828   {
829     /* error was posted */
830     gst_buffer_unref (buffer);
831     return GST_FLOW_ERROR;
832   }
833 invalid_quant:
834   {
835     GST_ELEMENT_ERROR (pay, STREAM, FORMAT, ("Invalid quant tables"), (NULL));
836     gst_buffer_unref (buffer);
837     return GST_FLOW_ERROR;
838   }
839 }
840
841 static void
842 gst_rtp_jpeg_pay_set_property (GObject * object, guint prop_id,
843     const GValue * value, GParamSpec * pspec)
844 {
845   GstRtpJPEGPay *rtpjpegpay;
846
847   rtpjpegpay = GST_RTP_JPEG_PAY (object);
848
849   switch (prop_id) {
850     case PROP_JPEG_QUALITY:
851       rtpjpegpay->quality = g_value_get_int (value);
852       GST_DEBUG_OBJECT (object, "quality = %d", rtpjpegpay->quality);
853       break;
854     case PROP_JPEG_TYPE:
855       rtpjpegpay->type = g_value_get_int (value);
856       GST_DEBUG_OBJECT (object, "type = %d", rtpjpegpay->type);
857       break;
858     case PROP_BUFFER_LIST:
859       rtpjpegpay->buffer_list = g_value_get_boolean (value);
860       GST_DEBUG_OBJECT (object, "buffer_list = %d", rtpjpegpay->buffer_list);
861       break;
862     default:
863       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
864       break;
865   }
866 }
867
868 static void
869 gst_rtp_jpeg_pay_get_property (GObject * object, guint prop_id,
870     GValue * value, GParamSpec * pspec)
871 {
872   GstRtpJPEGPay *rtpjpegpay;
873
874   rtpjpegpay = GST_RTP_JPEG_PAY (object);
875
876   switch (prop_id) {
877     case PROP_JPEG_QUALITY:
878       g_value_set_int (value, rtpjpegpay->quality);
879       break;
880     case PROP_JPEG_TYPE:
881       g_value_set_int (value, rtpjpegpay->type);
882       break;
883     case PROP_BUFFER_LIST:
884       g_value_set_boolean (value, rtpjpegpay->buffer_list);
885       break;
886     default:
887       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
888       break;
889   }
890 }
891
892 gboolean
893 gst_rtp_jpeg_pay_plugin_init (GstPlugin * plugin)
894 {
895   return gst_element_register (plugin, "rtpjpegpay", GST_RANK_NONE,
896       GST_TYPE_RTP_JPEG_PAY);
897 }