7688d2eafedeb87424a6614552f431811b4c6f6e
[platform/upstream/gst-plugins-good.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  *
92  * Identifers for markers in JPEG header
93  */
94 enum _RtpJpegMarker
95 {
96   JPEG_MARKER = 0xFF,
97   JPEG_MARKER_SOI = 0xD8,
98   JPEG_MARKER_JFIF = 0xE0,
99   JPEG_MARKER_CMT = 0xFE,
100   JPEG_MARKER_DQT = 0xDB,
101   JPEG_MARKER_SOF = 0xC0,
102   JPEG_MARKER_DHT = 0xC4,
103   JPEG_MARKER_SOS = 0xDA,
104   JPEG_MARKER_EOI = 0xD9,
105 };
106
107 #define DEFAULT_JPEG_QUANT    255
108
109 #define DEFAULT_JPEG_QUALITY  255
110 #define DEFAULT_JPEG_TYPE     1
111
112 enum
113 {
114   PROP_0,
115   PROP_JPEG_QUALITY,
116   PROP_JPEG_TYPE,
117   PROP_BUFFER_LIST,
118   PROP_LAST
119 };
120
121 enum
122 {
123   Q_TABLE_0 = 0,
124   Q_TABLE_1,
125   Q_TABLE_MAX                   /* only support for two tables at the moment */
126 };
127
128 typedef struct _RtpJpegHeader RtpJpegHeader;
129
130 /*
131  * RtpJpegHeader:
132  * @type_spec: type specific
133  * @offset: fragment offset
134  * @type: type field
135  * @q: quantization table for this frame
136  * @width: width of image in 8-pixel multiples
137  * @height: height of image in 8-pixel multiples
138  *
139  * 0                   1                   2                   3
140  * 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
141  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
142  * | Type-specific |              Fragment Offset                  |
143  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
144  * |      Type     |       Q       |     Width     |     Height    |
145  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
146  */
147 struct _RtpJpegHeader
148 {
149   guint type_spec:8;
150   guint offset:24;
151   guint8 type;
152   guint8 q;
153   guint8 width;
154   guint8 height;
155 };
156
157 /*
158  * RtpQuantHeader
159  * @mbz: must be zero
160  * @precision: specify size of quantization tables
161  * @length: length of quantization data
162  *
163  * 0                   1                   2                   3
164  * 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
165  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
166  * |      MBZ      |   Precision   |             Length            |
167  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
168  * |                    Quantization Table Data                    |
169  * |                              ...                              |
170  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
171  */
172 typedef struct
173 {
174   guint8 mbz;
175   guint8 precision;
176   guint16 length;
177 } RtpQuantHeader;
178
179 typedef struct
180 {
181   guint8 size;
182   const guint8 *data;
183 } RtpQuantTable;
184
185 typedef struct
186 {
187   guint8 id;
188   guint8 samp;
189   guint8 qt;
190 } CompInfo;
191
192 /* FIXME: restart marker header currently unsupported */
193
194 static void gst_rtp_jpeg_pay_set_property (GObject * object, guint prop_id,
195     const GValue * value, GParamSpec * pspec);
196
197 static void gst_rtp_jpeg_pay_get_property (GObject * object, guint prop_id,
198     GValue * value, GParamSpec * pspec);
199
200 static gboolean gst_rtp_jpeg_pay_setcaps (GstBaseRTPPayload * basepayload,
201     GstCaps * caps);
202
203 static GstFlowReturn gst_rtp_jpeg_pay_handle_buffer (GstBaseRTPPayload * pad,
204     GstBuffer * buffer);
205
206 GST_BOILERPLATE (GstRtpJPEGPay, gst_rtp_jpeg_pay, GstBaseRTPPayload,
207     GST_TYPE_BASE_RTP_PAYLOAD);
208
209 static void
210 gst_rtp_jpeg_pay_base_init (gpointer klass)
211 {
212   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
213
214   gst_element_class_add_pad_template (element_class,
215       gst_static_pad_template_get (&gst_rtp_jpeg_pay_src_template));
216   gst_element_class_add_pad_template (element_class,
217       gst_static_pad_template_get (&gst_rtp_jpeg_pay_sink_template));
218
219   gst_element_class_set_details_simple (element_class, "RTP JPEG payloader",
220       "Codec/Payloader/Network",
221       "Payload-encodes JPEG pictures into RTP packets (RFC 2435)",
222       "Axis Communications <dev-gstreamer@axis.com>");
223 }
224
225 static void
226 gst_rtp_jpeg_pay_class_init (GstRtpJPEGPayClass * klass)
227 {
228   GObjectClass *gobject_class;
229   GstBaseRTPPayloadClass *gstbasertppayload_class;
230
231   gobject_class = (GObjectClass *) klass;
232   gstbasertppayload_class = (GstBaseRTPPayloadClass *) klass;
233
234   gobject_class->set_property = gst_rtp_jpeg_pay_set_property;
235   gobject_class->get_property = gst_rtp_jpeg_pay_get_property;
236
237   gstbasertppayload_class->set_caps = gst_rtp_jpeg_pay_setcaps;
238   gstbasertppayload_class->handle_buffer = gst_rtp_jpeg_pay_handle_buffer;
239
240   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_JPEG_QUALITY,
241       g_param_spec_int ("quality", "Quality",
242           "Quality factor on JPEG data (unused)", 0, 255, 255,
243           G_PARAM_READWRITE));
244
245   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_JPEG_TYPE,
246       g_param_spec_int ("type", "Type",
247           "Default JPEG Type, overwritten by SOF when present", 0, 255,
248           DEFAULT_JPEG_TYPE, G_PARAM_READWRITE));
249
250   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BUFFER_LIST,
251       g_param_spec_boolean ("buffer-list", "Buffer List",
252           "Use Buffer Lists",
253           DEFAULT_BUFFER_LIST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
254
255   GST_DEBUG_CATEGORY_INIT (rtpjpegpay_debug, "rtpjpegpay", 0,
256       "Motion JPEG RTP Payloader");
257 }
258
259 static void
260 gst_rtp_jpeg_pay_init (GstRtpJPEGPay * pay, GstRtpJPEGPayClass * klass)
261 {
262   pay->quality = DEFAULT_JPEG_QUALITY;
263   pay->quant = DEFAULT_JPEG_QUANT;
264   pay->type = DEFAULT_JPEG_TYPE;
265   pay->buffer_list = DEFAULT_BUFFER_LIST;
266 }
267
268 static gboolean
269 gst_rtp_jpeg_pay_setcaps (GstBaseRTPPayload * basepayload, GstCaps * caps)
270 {
271   GstStructure *caps_structure = gst_caps_get_structure (caps, 0);
272   GstRtpJPEGPay *pay;
273   gboolean res;
274   gint width = 0, height = 0;
275
276   pay = GST_RTP_JPEG_PAY (basepayload);
277
278   /* these properties are not mandatory, we can get them from the SOF, if there
279    * is one. */
280   if (gst_structure_get_int (caps_structure, "height", &height)) {
281     if (height <= 0 || height > 2040)
282       goto invalid_dimension;
283   }
284   pay->height = height / 8;
285
286   if (gst_structure_get_int (caps_structure, "width", &width)) {
287     if (width <= 0 || width > 2040)
288       goto invalid_dimension;
289   }
290   pay->width = width / 8;
291
292   gst_basertppayload_set_options (basepayload, "video", TRUE, "JPEG", 90000);
293   res = gst_basertppayload_set_outcaps (basepayload, NULL);
294
295   return res;
296
297   /* ERRORS */
298 invalid_dimension:
299   {
300     GST_ERROR_OBJECT (pay, "Invalid width/height from caps");
301     return FALSE;
302   }
303 }
304
305 static guint
306 gst_rtp_jpeg_pay_header_size (const guint8 * data, guint offset)
307 {
308   return data[offset] << 8 | data[offset + 1];
309 }
310
311 static guint
312 gst_rtp_jpeg_pay_read_quant_table (const guint8 * data, guint size,
313     guint offset, RtpQuantTable tables[])
314 {
315   guint quant_size, tab_size;
316   guint8 prec;
317   guint8 id;
318
319   if (offset + 2 > size)
320     return size;
321
322   quant_size = gst_rtp_jpeg_pay_header_size (data, offset);
323   if (quant_size < 2)
324     return size;
325
326   /* clamp to available data */
327   if (offset + quant_size > size)
328     quant_size = size - offset;
329
330   offset += 2;
331   quant_size -= 2;
332
333   while (quant_size > 0) {
334     /* not enough to read the id */
335     if (offset + 1 > size)
336       break;
337
338     id = data[offset] & 0xf;
339     if (id == 15)
340       /* invalid id received - corrupt data */
341       break;
342
343     prec = (data[offset] & 0xf0) >> 4;
344     if (prec)
345       tab_size = 128;
346     else
347       tab_size = 64;
348
349     /* there is not enough for the table */
350     if (quant_size < tab_size + 1)
351       break;
352
353     GST_LOG ("read quant table %d, tab_size %d, prec %02x", id, tab_size, prec);
354
355     tables[id].size = tab_size;
356     tables[id].data = &data[offset + 1];
357
358     tab_size += 1;
359     quant_size -= tab_size;
360     offset += tab_size;
361   }
362   return offset + quant_size;
363 }
364
365 static gboolean
366 gst_rtp_jpeg_pay_read_sof (GstRtpJPEGPay * pay, const guint8 * data,
367     guint size, guint * offset, CompInfo info[])
368 {
369   guint sof_size, off;
370   guint width, height, infolen;
371   CompInfo elem;
372   gint i, j;
373
374   off = *offset;
375
376   /* we need at least 17 bytes for the SOF */
377   if (off + 17 > size)
378     goto wrong_length;
379
380   sof_size = gst_rtp_jpeg_pay_header_size (data, off);
381   if (sof_size < 17)
382     goto wrong_length;
383
384   *offset += sof_size;
385
386   /* skip size */
387   off += 2;
388
389   /* precision should be 8 */
390   if (data[off++] != 8)
391     goto bad_precision;
392
393   /* read dimensions */
394   height = data[off] << 8 | data[off + 1];
395   width = data[off + 2] << 8 | data[off + 3];
396   off += 4;
397
398   GST_LOG_OBJECT (pay, "got dimensions %ux%u", height, width);
399
400   if (height == 0 || height > 2040)
401     goto invalid_dimension;
402   if (width == 0 || width > 2040)
403     goto invalid_dimension;
404
405   pay->height = height / 8;
406   pay->width = width / 8;
407
408   /* we only support 3 components */
409   if (data[off++] != 3)
410     goto bad_components;
411
412   infolen = 0;
413   for (i = 0; i < 3; i++) {
414     elem.id = data[off++];
415     elem.samp = data[off++];
416     elem.qt = data[off++];
417     GST_LOG_OBJECT (pay, "got comp %d, samp %02x, qt %d", elem.id, elem.samp,
418         elem.qt);
419     /* insertion sort from the last element to the first */
420     for (j = infolen; j > 1; j--) {
421       if (G_LIKELY (info[j - 1].id < elem.id))
422         break;
423       info[j] = info[j - 1];
424     }
425     info[j] = elem;
426     infolen++;
427   }
428
429   /* see that the components are supported */
430   if (info[0].samp == 0x21)
431     pay->type = 0;
432   else if (info[0].samp == 0x22)
433     pay->type = 1;
434   else
435     goto invalid_comp;
436
437   /* the other components are free to use any quant table but they have to
438    * have the same table id */
439   if (!(info[1].samp == 0x11))
440     goto invalid_comp;
441
442   if (!(info[2].samp == 0x11))
443     goto invalid_comp;
444
445   if (info[1].qt != info[2].qt)
446     goto invalid_comp;
447
448   return TRUE;
449
450   /* ERRORS */
451 wrong_length:
452   {
453     GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
454         ("Wrong SOF length %u.", sof_size), (NULL));
455     return FALSE;
456   }
457 bad_precision:
458   {
459     GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
460         ("Wrong precision, expecting 8."), (NULL));
461     return FALSE;
462   }
463 invalid_dimension:
464   {
465     GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
466         ("Wrong dimension, size %ux%u", width, height), (NULL));
467     return FALSE;
468   }
469 bad_components:
470   {
471     GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
472         ("Wrong number of components"), (NULL));
473     return FALSE;
474   }
475 invalid_comp:
476   {
477     GST_ELEMENT_ERROR (pay, STREAM, FORMAT, ("Invalid component"), (NULL));
478     return FALSE;
479   }
480 }
481
482 static RtpJpegMarker
483 gst_rtp_jpeg_pay_scan_marker (const guint8 * data, guint size, guint * offset)
484 {
485   while ((data[(*offset)++] != JPEG_MARKER) && ((*offset) < size));
486
487   if (G_UNLIKELY ((*offset) >= size)) {
488     GST_LOG ("found EOI marker");
489     return JPEG_MARKER_EOI;
490   } else {
491     guint8 marker;
492
493     marker = data[*offset];
494     GST_LOG ("found %02x marker at offset %u", marker, *offset);
495     (*offset)++;
496     return marker;
497   }
498 }
499
500 static GstFlowReturn
501 gst_rtp_jpeg_pay_handle_buffer (GstBaseRTPPayload * basepayload,
502     GstBuffer * buffer)
503 {
504   GstRtpJPEGPay *pay;
505   GstClockTime timestamp;
506   GstFlowReturn ret = GST_FLOW_ERROR;
507   RtpJpegHeader jpeg_header;
508   RtpQuantHeader quant_header;
509   RtpQuantTable tables[15] = { {0, NULL}, };
510   CompInfo info[3] = { {0,}, };
511   guint quant_data_size;
512   guint8 *data;
513   guint size;
514   guint mtu;
515   guint bytes_left;
516   guint jpeg_header_size = 0;
517   guint offset;
518   gboolean frame_done;
519   gboolean sos_found, sof_found, dqt_found;
520   gint i;
521   GstBufferList *list = NULL;
522   GstBufferListIterator *it = NULL;
523
524   pay = GST_RTP_JPEG_PAY (basepayload);
525   mtu = GST_BASE_RTP_PAYLOAD_MTU (pay);
526
527   size = GST_BUFFER_SIZE (buffer);
528   data = GST_BUFFER_DATA (buffer);
529   timestamp = GST_BUFFER_TIMESTAMP (buffer);
530   offset = 0;
531
532   GST_LOG_OBJECT (pay, "got buffer size %u, timestamp %" GST_TIME_FORMAT, size,
533       GST_TIME_ARGS (timestamp));
534
535   /* parse the jpeg header for 'start of scan' and read quant tables if needed */
536   sos_found = FALSE;
537   dqt_found = FALSE;
538   sof_found = FALSE;
539
540   while (!sos_found && (offset < size)) {
541     GST_LOG_OBJECT (pay, "checking from offset %u", offset);
542     switch (gst_rtp_jpeg_pay_scan_marker (data, size, &offset)) {
543       case JPEG_MARKER_JFIF:
544       case JPEG_MARKER_CMT:
545       case JPEG_MARKER_DHT:
546         GST_LOG_OBJECT (pay, "skipping marker");
547         offset += gst_rtp_jpeg_pay_header_size (data, offset);
548         break;
549       case JPEG_MARKER_SOF:
550         if (!gst_rtp_jpeg_pay_read_sof (pay, data, size, &offset, info))
551           goto invalid_format;
552         sof_found = TRUE;
553         break;
554       case JPEG_MARKER_DQT:
555         GST_LOG ("DQT found");
556         offset = gst_rtp_jpeg_pay_read_quant_table (data, size, offset, tables);
557         dqt_found = TRUE;
558         break;
559       case JPEG_MARKER_SOS:
560         sos_found = TRUE;
561         GST_LOG_OBJECT (pay, "SOS found");
562         jpeg_header_size = offset + gst_rtp_jpeg_pay_header_size (data, offset);
563         break;
564       case JPEG_MARKER_EOI:
565         GST_WARNING_OBJECT (pay, "EOI reached before SOS!");
566         break;
567       case JPEG_MARKER_SOI:
568         GST_LOG_OBJECT (pay, "SOI found");
569         break;
570       default:
571         break;
572     }
573   }
574   if (!dqt_found || !sof_found)
575     goto unsupported_jpeg;
576
577   /* by now we should either have negotiated the width/height or the SOF header
578    * should have filled us in */
579   if (pay->width == 0 || pay->height == 0)
580     goto no_dimension;
581
582   GST_LOG_OBJECT (pay, "header size %u", jpeg_header_size);
583
584   size -= jpeg_header_size;
585   data += jpeg_header_size;
586   offset = 0;
587
588   /* prepare stuff for the jpeg header */
589   jpeg_header.type_spec = 0;
590   jpeg_header.type = pay->type;
591   jpeg_header.q = pay->quant;
592   jpeg_header.width = pay->width;
593   jpeg_header.height = pay->height;
594
595   /* collect the quant headers sizes */
596   quant_header.mbz = 0;
597   quant_header.precision = 0;
598   quant_header.length = 0;
599   quant_data_size = 0;
600
601   if (pay->quant > 127) {
602     /* for the Y and U component, look up the quant table and its size. quant
603      * tables for U and V should be the same */
604     for (i = 0; i < 2; i++) {
605       guint qsize;
606       guint qt;
607
608       qt = info[i].qt;
609       if (qt > 15)
610         goto invalid_quant;
611
612       qsize = tables[qt].size;
613       if (qsize == 0)
614         goto invalid_quant;
615
616       quant_header.precision |= (qsize == 64 ? 0 : (1 << i));
617       quant_data_size += qsize;
618     }
619     quant_header.length = g_htons (quant_data_size);
620     quant_data_size += sizeof (quant_header);
621   }
622
623   GST_LOG_OBJECT (pay, "quant_data size %u", quant_data_size);
624
625   if (pay->buffer_list) {
626     list = gst_buffer_list_new ();
627     it = gst_buffer_list_iterate (list);
628   }
629
630   bytes_left = sizeof (jpeg_header) + quant_data_size + size;
631
632   frame_done = FALSE;
633   do {
634     GstBuffer *outbuf;
635     guint8 *payload;
636     guint payload_size = (bytes_left < mtu ? bytes_left : mtu);
637
638     if (pay->buffer_list) {
639       outbuf = gst_rtp_buffer_new_allocate (sizeof (jpeg_header) +
640           quant_data_size, 0, 0);
641     } else {
642       outbuf = gst_rtp_buffer_new_allocate (payload_size, 0, 0);
643     }
644     GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
645
646     if (payload_size == bytes_left) {
647       GST_LOG_OBJECT (pay, "last packet of frame");
648       frame_done = TRUE;
649       gst_rtp_buffer_set_marker (outbuf, 1);
650     }
651
652     payload = gst_rtp_buffer_get_payload (outbuf);
653
654     /* update offset */
655 #if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
656     jpeg_header.offset = ((offset & 0x0000FF) << 16) |
657         ((offset & 0xFF0000) >> 16) | (offset & 0x00FF00);
658 #else
659     jpeg_header.offset = offset;
660 #endif
661     memcpy (payload, &jpeg_header, sizeof (jpeg_header));
662     payload += sizeof (jpeg_header);
663     payload_size -= sizeof (jpeg_header);
664
665     /* only send quant table with first packet */
666     if (G_UNLIKELY (quant_data_size > 0)) {
667       memcpy (payload, &quant_header, sizeof (quant_header));
668       payload += sizeof (quant_header);
669
670       /* copy the quant tables for luma and chrominance */
671       for (i = 0; i < 2; i++) {
672         guint qsize;
673         guint qt;
674
675         qt = info[i].qt;
676         qsize = tables[qt].size;
677         memcpy (payload, tables[qt].data, qsize);
678
679         GST_LOG_OBJECT (pay, "component %d using quant %d, size %d", i, qt,
680             qsize);
681
682         payload += qsize;
683       }
684       payload_size -= quant_data_size;
685       bytes_left -= quant_data_size;
686       quant_data_size = 0;
687     }
688     GST_LOG_OBJECT (pay, "sending payload size %d", payload_size);
689
690     if (pay->buffer_list) {
691       GstBuffer *paybuf;
692
693       /* create a new buf to hold the payload */
694       paybuf = gst_buffer_create_sub (buffer, jpeg_header_size + offset,
695           payload_size);
696
697       /* create a new group to hold the rtp header and the payload */
698       gst_buffer_list_iterator_add_group (it);
699       gst_buffer_list_iterator_add (it, outbuf);
700       gst_buffer_list_iterator_add (it, paybuf);
701     } else {
702       memcpy (payload, data, payload_size);
703       ret = gst_basertppayload_push (basepayload, outbuf);
704       if (ret != GST_FLOW_OK)
705         break;
706     }
707
708     bytes_left -= payload_size;
709     offset += payload_size;
710     data += payload_size;
711   }
712   while (!frame_done);
713
714   if (pay->buffer_list) {
715     gst_buffer_list_iterator_free (it);
716     /* push the whole buffer list at once */
717     ret = gst_basertppayload_push_list (basepayload, list);
718   }
719
720   gst_buffer_unref (buffer);
721
722   return ret;
723
724   /* ERRORS */
725 unsupported_jpeg:
726   {
727     GST_ELEMENT_ERROR (pay, STREAM, FORMAT, ("Unsupported JPEG"), (NULL));
728     gst_buffer_unref (buffer);
729     return GST_FLOW_NOT_SUPPORTED;
730   }
731 no_dimension:
732   {
733     GST_ELEMENT_ERROR (pay, STREAM, FORMAT, ("No size given"), (NULL));
734     gst_buffer_unref (buffer);
735     return GST_FLOW_NOT_NEGOTIATED;
736   }
737 invalid_format:
738   {
739     /* error was posted */
740     gst_buffer_unref (buffer);
741     return GST_FLOW_ERROR;
742   }
743 invalid_quant:
744   {
745     GST_ELEMENT_ERROR (pay, STREAM, FORMAT, ("Invalid quant tables"), (NULL));
746     gst_buffer_unref (buffer);
747     return GST_FLOW_ERROR;
748   }
749 }
750
751 static void
752 gst_rtp_jpeg_pay_set_property (GObject * object, guint prop_id,
753     const GValue * value, GParamSpec * pspec)
754 {
755   GstRtpJPEGPay *rtpjpegpay;
756
757   rtpjpegpay = GST_RTP_JPEG_PAY (object);
758
759   switch (prop_id) {
760     case PROP_JPEG_QUALITY:
761       rtpjpegpay->quality = g_value_get_int (value);
762       GST_DEBUG_OBJECT (object, "quality = %d", rtpjpegpay->quality);
763       break;
764     case PROP_JPEG_TYPE:
765       rtpjpegpay->type = g_value_get_int (value);
766       GST_DEBUG_OBJECT (object, "type = %d", rtpjpegpay->type);
767       break;
768     case PROP_BUFFER_LIST:
769       rtpjpegpay->buffer_list = g_value_get_boolean (value);
770       GST_DEBUG_OBJECT (object, "buffer_list = %d", rtpjpegpay->buffer_list);
771       break;
772     default:
773       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
774       break;
775   }
776 }
777
778 static void
779 gst_rtp_jpeg_pay_get_property (GObject * object, guint prop_id,
780     GValue * value, GParamSpec * pspec)
781 {
782   GstRtpJPEGPay *rtpjpegpay;
783
784   rtpjpegpay = GST_RTP_JPEG_PAY (object);
785
786   switch (prop_id) {
787     case PROP_JPEG_QUALITY:
788       g_value_set_int (value, rtpjpegpay->quality);
789       break;
790     case PROP_JPEG_TYPE:
791       g_value_set_int (value, rtpjpegpay->type);
792       break;
793     case PROP_BUFFER_LIST:
794       g_value_set_boolean (value, rtpjpegpay->buffer_list);
795       break;
796     default:
797       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
798       break;
799   }
800 }
801
802 gboolean
803 gst_rtp_jpeg_pay_plugin_init (GstPlugin * plugin)
804 {
805   return gst_element_register (plugin, "rtpjpegpay", GST_RANK_NONE,
806       GST_TYPE_RTP_JPEG_PAY);
807 }