Merge remote-tracking branch 'origin/master' into 0.11
[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  * @JPEG_MARKER_DRI: Define Restart Interval marker
92  * @JPEG_MARKER_H264: H264 marker
93  *
94  * Identifers for markers in JPEG header
95  */
96 enum _RtpJpegMarker
97 {
98   JPEG_MARKER = 0xFF,
99   JPEG_MARKER_SOI = 0xD8,
100   JPEG_MARKER_JFIF = 0xE0,
101   JPEG_MARKER_CMT = 0xFE,
102   JPEG_MARKER_DQT = 0xDB,
103   JPEG_MARKER_SOF = 0xC0,
104   JPEG_MARKER_DHT = 0xC4,
105   JPEG_MARKER_SOS = 0xDA,
106   JPEG_MARKER_EOI = 0xD9,
107   JPEG_MARKER_DRI = 0xDD,
108   JPEG_MARKER_H264 = 0xE4
109 };
110
111 #define DEFAULT_JPEG_QUANT    255
112
113 #define DEFAULT_JPEG_QUALITY  255
114 #define DEFAULT_JPEG_TYPE     1
115
116 enum
117 {
118   PROP_0,
119   PROP_JPEG_QUALITY,
120   PROP_JPEG_TYPE,
121   PROP_BUFFER_LIST,
122   PROP_LAST
123 };
124
125 enum
126 {
127   Q_TABLE_0 = 0,
128   Q_TABLE_1,
129   Q_TABLE_MAX                   /* only support for two tables at the moment */
130 };
131
132 typedef struct _RtpJpegHeader RtpJpegHeader;
133
134 /*
135  * RtpJpegHeader:
136  * @type_spec: type specific
137  * @offset: fragment offset
138  * @type: type field
139  * @q: quantization table for this frame
140  * @width: width of image in 8-pixel multiples
141  * @height: height of image in 8-pixel multiples
142  *
143  * 0                   1                   2                   3
144  * 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
145  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
146  * | Type-specific |              Fragment Offset                  |
147  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
148  * |      Type     |       Q       |     Width     |     Height    |
149  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
150  */
151 struct _RtpJpegHeader
152 {
153   guint type_spec:8;
154   guint offset:24;
155   guint8 type;
156   guint8 q;
157   guint8 width;
158   guint8 height;
159 };
160
161 /*
162  * RtpQuantHeader
163  * @mbz: must be zero
164  * @precision: specify size of quantization tables
165  * @length: length of quantization data
166  *
167  * 0                   1                   2                   3
168  * 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
169  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
170  * |      MBZ      |   Precision   |             Length            |
171  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
172  * |                    Quantization Table Data                    |
173  * |                              ...                              |
174  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
175  */
176 typedef struct
177 {
178   guint8 mbz;
179   guint8 precision;
180   guint16 length;
181 } RtpQuantHeader;
182
183 typedef struct
184 {
185   guint8 size;
186   const guint8 *data;
187 } RtpQuantTable;
188
189 /*
190  * RtpRestartMarkerHeader:
191  * @restartInterval: number of MCUs that appear between restart markers
192  * @restartFirstLastCount: a combination of the first packet mark in the chunk
193  *                         last packet mark in the chunk and the position of the
194  *                         first restart interval in the current "chunk"
195  *
196  *    0                   1                   2                   3
197  *   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
198  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
199  *  |       Restart Interval        |F|L|       Restart Count       |
200  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
201  *
202  *  The restart marker header is implemented according to the following
203  *  methodology specified in section 3.1.7 of rfc2435.txt.
204  *
205  *  "If the restart intervals in a frame are not guaranteed to be aligned
206  *  with packet boundaries, the F (first) and L (last) bits MUST be set
207  *  to 1 and the Restart Count MUST be set to 0x3FFF.  This indicates
208  *  that a receiver MUST reassemble the entire frame before decoding it."
209  *
210  */
211
212 typedef struct
213 {
214   guint16 restart_interval;
215   guint16 restart_count;
216 } RtpRestartMarkerHeader;
217
218 typedef struct
219 {
220   guint8 id;
221   guint8 samp;
222   guint8 qt;
223 } CompInfo;
224
225 /* FIXME: restart marker header currently unsupported */
226
227 static void gst_rtp_jpeg_pay_set_property (GObject * object, guint prop_id,
228     const GValue * value, GParamSpec * pspec);
229
230 static void gst_rtp_jpeg_pay_get_property (GObject * object, guint prop_id,
231     GValue * value, GParamSpec * pspec);
232
233 static gboolean gst_rtp_jpeg_pay_setcaps (GstRTPBasePayload * basepayload,
234     GstCaps * caps);
235
236 static GstFlowReturn gst_rtp_jpeg_pay_handle_buffer (GstRTPBasePayload * pad,
237     GstBuffer * buffer);
238
239 #define gst_rtp_jpeg_pay_parent_class parent_class
240 G_DEFINE_TYPE (GstRtpJPEGPay, gst_rtp_jpeg_pay, GST_TYPE_RTP_BASE_PAYLOAD);
241
242 static void
243 gst_rtp_jpeg_pay_class_init (GstRtpJPEGPayClass * klass)
244 {
245   GObjectClass *gobject_class;
246   GstElementClass *gstelement_class;
247   GstRTPBasePayloadClass *gstrtpbasepayload_class;
248
249   gobject_class = (GObjectClass *) klass;
250   gstelement_class = (GstElementClass *) klass;
251   gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass;
252
253   gobject_class->set_property = gst_rtp_jpeg_pay_set_property;
254   gobject_class->get_property = gst_rtp_jpeg_pay_get_property;
255
256   gst_element_class_add_pad_template (gstelement_class,
257       gst_static_pad_template_get (&gst_rtp_jpeg_pay_src_template));
258   gst_element_class_add_pad_template (gstelement_class,
259       gst_static_pad_template_get (&gst_rtp_jpeg_pay_sink_template));
260
261   gst_element_class_set_details_simple (gstelement_class, "RTP JPEG payloader",
262       "Codec/Payloader/Network/RTP",
263       "Payload-encodes JPEG pictures into RTP packets (RFC 2435)",
264       "Axis Communications <dev-gstreamer@axis.com>");
265
266   gstrtpbasepayload_class->set_caps = gst_rtp_jpeg_pay_setcaps;
267   gstrtpbasepayload_class->handle_buffer = gst_rtp_jpeg_pay_handle_buffer;
268
269   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_JPEG_QUALITY,
270       g_param_spec_int ("quality", "Quality",
271           "Quality factor on JPEG data (unused)", 0, 255, 255,
272           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
273
274   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_JPEG_TYPE,
275       g_param_spec_int ("type", "Type",
276           "Default JPEG Type, overwritten by SOF when present", 0, 255,
277           DEFAULT_JPEG_TYPE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
278
279   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BUFFER_LIST,
280       g_param_spec_boolean ("buffer-list", "Buffer List",
281           "Use Buffer Lists",
282           DEFAULT_BUFFER_LIST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
283
284   GST_DEBUG_CATEGORY_INIT (rtpjpegpay_debug, "rtpjpegpay", 0,
285       "Motion JPEG RTP Payloader");
286 }
287
288 static void
289 gst_rtp_jpeg_pay_init (GstRtpJPEGPay * pay)
290 {
291   pay->quality = DEFAULT_JPEG_QUALITY;
292   pay->quant = DEFAULT_JPEG_QUANT;
293   pay->type = DEFAULT_JPEG_TYPE;
294   pay->buffer_list = DEFAULT_BUFFER_LIST;
295 }
296
297 static gboolean
298 gst_rtp_jpeg_pay_setcaps (GstRTPBasePayload * basepayload, GstCaps * caps)
299 {
300   GstStructure *caps_structure = gst_caps_get_structure (caps, 0);
301   GstRtpJPEGPay *pay;
302   gboolean res;
303   gint width = 0, height = 0;
304
305   pay = GST_RTP_JPEG_PAY (basepayload);
306
307   /* these properties are not mandatory, we can get them from the SOF, if there
308    * is one. */
309   if (gst_structure_get_int (caps_structure, "height", &height)) {
310     if (height <= 0 || height > 2040)
311       goto invalid_dimension;
312   }
313   pay->height = GST_ROUND_UP_8 (height) / 8;
314
315   if (gst_structure_get_int (caps_structure, "width", &width)) {
316     if (width <= 0 || width > 2040)
317       goto invalid_dimension;
318   }
319   pay->width = GST_ROUND_UP_8 (width) / 8;
320
321   gst_rtp_base_payload_set_options (basepayload, "video", TRUE, "JPEG", 90000);
322   res = gst_rtp_base_payload_set_outcaps (basepayload, NULL);
323
324   return res;
325
326   /* ERRORS */
327 invalid_dimension:
328   {
329     GST_ERROR_OBJECT (pay, "Invalid width/height from caps");
330     return FALSE;
331   }
332 }
333
334 static guint
335 gst_rtp_jpeg_pay_header_size (const guint8 * data, guint offset)
336 {
337   return data[offset] << 8 | data[offset + 1];
338 }
339
340 static guint
341 gst_rtp_jpeg_pay_read_quant_table (const guint8 * data, guint size,
342     guint offset, RtpQuantTable tables[])
343 {
344   guint quant_size, tab_size;
345   guint8 prec;
346   guint8 id;
347
348   if (offset + 2 > size)
349     goto too_small;
350
351   quant_size = gst_rtp_jpeg_pay_header_size (data, offset);
352   if (quant_size < 2)
353     goto small_quant_size;
354
355   /* clamp to available data */
356   if (offset + quant_size > size)
357     quant_size = size - offset;
358
359   offset += 2;
360   quant_size -= 2;
361
362   while (quant_size > 0) {
363     /* not enough to read the id */
364     if (offset + 1 > size)
365       break;
366
367     id = data[offset] & 0x0f;
368     if (id == 15)
369       /* invalid id received - corrupt data */
370       goto invalid_id;
371
372     prec = (data[offset] & 0xf0) >> 4;
373     if (prec)
374       tab_size = 128;
375     else
376       tab_size = 64;
377
378     /* there is not enough for the table */
379     if (quant_size < tab_size + 1)
380       goto no_table;
381
382     GST_LOG ("read quant table %d, tab_size %d, prec %02x", id, tab_size, prec);
383
384     tables[id].size = tab_size;
385     tables[id].data = &data[offset + 1];
386
387     tab_size += 1;
388     quant_size -= tab_size;
389     offset += tab_size;
390   }
391 done:
392   return offset + quant_size;
393
394   /* ERRORS */
395 too_small:
396   {
397     GST_WARNING ("not enough data");
398     return size;
399   }
400 small_quant_size:
401   {
402     GST_WARNING ("quant_size too small (%u < 2)", quant_size);
403     return size;
404   }
405 invalid_id:
406   {
407     GST_WARNING ("invalid id");
408     goto done;
409   }
410 no_table:
411   {
412     GST_WARNING ("not enough data for table (%u < %u)", quant_size,
413         tab_size + 1);
414     goto done;
415   }
416 }
417
418 static gboolean
419 gst_rtp_jpeg_pay_read_sof (GstRtpJPEGPay * pay, const guint8 * data,
420     guint size, guint * offset, CompInfo info[])
421 {
422   guint sof_size, off;
423   guint width, height, infolen;
424   CompInfo elem;
425   gint i, j;
426
427   off = *offset;
428
429   /* we need at least 17 bytes for the SOF */
430   if (off + 17 > size)
431     goto wrong_size;
432
433   sof_size = gst_rtp_jpeg_pay_header_size (data, off);
434   if (sof_size < 17)
435     goto wrong_length;
436
437   *offset += sof_size;
438
439   /* skip size */
440   off += 2;
441
442   /* precision should be 8 */
443   if (data[off++] != 8)
444     goto bad_precision;
445
446   /* read dimensions */
447   height = data[off] << 8 | data[off + 1];
448   width = data[off + 2] << 8 | data[off + 3];
449   off += 4;
450
451   GST_LOG_OBJECT (pay, "got dimensions %ux%u", height, width);
452
453   if (height == 0 || height > 2040)
454     goto invalid_dimension;
455   if (width == 0 || width > 2040)
456     goto invalid_dimension;
457
458   pay->height = GST_ROUND_UP_8 (height) / 8;
459   pay->width = GST_ROUND_UP_8 (width) / 8;
460
461   /* we only support 3 components */
462   if (data[off++] != 3)
463     goto bad_components;
464
465   infolen = 0;
466   for (i = 0; i < 3; i++) {
467     elem.id = data[off++];
468     elem.samp = data[off++];
469     elem.qt = data[off++];
470     GST_LOG_OBJECT (pay, "got comp %d, samp %02x, qt %d", elem.id, elem.samp,
471         elem.qt);
472     /* insertion sort from the last element to the first */
473     for (j = infolen; j > 1; j--) {
474       if (G_LIKELY (info[j - 1].id < elem.id))
475         break;
476       info[j] = info[j - 1];
477     }
478     info[j] = elem;
479     infolen++;
480   }
481
482   /* see that the components are supported */
483   if (info[0].samp == 0x21)
484     pay->type = 0;
485   else if (info[0].samp == 0x22)
486     pay->type = 1;
487   else
488     goto invalid_comp;
489
490   if (!(info[1].samp == 0x11))
491     goto invalid_comp;
492
493   if (!(info[2].samp == 0x11))
494     goto invalid_comp;
495
496   /* the other components are free to use any quant table but they have to
497    * have the same table id */
498   if (info[1].qt != info[2].qt)
499     goto invalid_comp;
500
501   return TRUE;
502
503   /* ERRORS */
504 wrong_size:
505   {
506     GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
507         ("Wrong size %u (needed %u).", size, off + 17), (NULL));
508     return FALSE;
509   }
510 wrong_length:
511   {
512     GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
513         ("Wrong SOF length %u.", sof_size), (NULL));
514     return FALSE;
515   }
516 bad_precision:
517   {
518     GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
519         ("Wrong precision, expecting 8."), (NULL));
520     return FALSE;
521   }
522 invalid_dimension:
523   {
524     GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
525         ("Wrong dimension, size %ux%u", width, height), (NULL));
526     return FALSE;
527   }
528 bad_components:
529   {
530     GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
531         ("Wrong number of components"), (NULL));
532     return FALSE;
533   }
534 invalid_comp:
535   {
536     GST_ELEMENT_ERROR (pay, STREAM, FORMAT, ("Invalid component"), (NULL));
537     return FALSE;
538   }
539 }
540
541 static gboolean
542 gst_rtp_jpeg_pay_read_dri (GstRtpJPEGPay * pay, const guint8 * data,
543     guint size, guint * offset, RtpRestartMarkerHeader * dri)
544 {
545   guint dri_size, off;
546
547   off = *offset;
548
549   /* we need at least 4 bytes for the DRI */
550   if (off + 4 > size)
551     goto wrong_size;
552
553   dri_size = gst_rtp_jpeg_pay_header_size (data, off);
554   if (dri_size < 4)
555     goto wrong_length;
556
557   *offset += dri_size;
558   off += 2;
559
560   dri->restart_interval = g_htons ((data[off] << 8) | (data[off + 1]));
561   dri->restart_count = g_htons (0xFFFF);
562
563   return dri->restart_interval > 0;
564
565 wrong_size:
566   {
567     GST_WARNING ("not enough data for DRI");
568     *offset = size;
569     return FALSE;
570   }
571 wrong_length:
572   {
573     GST_WARNING ("DRI size too small (%u)", dri_size);
574     *offset += dri_size;
575     return FALSE;
576   }
577 }
578
579 static RtpJpegMarker
580 gst_rtp_jpeg_pay_scan_marker (const guint8 * data, guint size, guint * offset)
581 {
582   while ((data[(*offset)++] != JPEG_MARKER) && ((*offset) < size));
583
584   if (G_UNLIKELY ((*offset) >= size)) {
585     GST_LOG ("found EOI marker");
586     return JPEG_MARKER_EOI;
587   } else {
588     guint8 marker;
589
590     marker = data[*offset];
591     GST_LOG ("found 0x%02x marker at offset %u", marker, *offset);
592     (*offset)++;
593     return marker;
594   }
595 }
596
597 static GstFlowReturn
598 gst_rtp_jpeg_pay_handle_buffer (GstRTPBasePayload * basepayload,
599     GstBuffer * buffer)
600 {
601   GstRtpJPEGPay *pay;
602   GstClockTime timestamp;
603   GstFlowReturn ret = GST_FLOW_ERROR;
604   RtpJpegHeader jpeg_header;
605   RtpQuantHeader quant_header;
606   RtpRestartMarkerHeader restart_marker_header;
607   RtpQuantTable tables[15] = { {0, NULL}, };
608   CompInfo info[3] = { {0,}, };
609   guint quant_data_size;
610   GstMapInfo map;
611   guint8 *data;
612   gsize size;
613   guint mtu;
614   guint bytes_left;
615   guint jpeg_header_size = 0;
616   guint offset;
617   gboolean frame_done;
618   gboolean sos_found, sof_found, dqt_found, dri_found;
619   gint i;
620   GstBufferList *list = NULL;
621
622   pay = GST_RTP_JPEG_PAY (basepayload);
623   mtu = GST_RTP_BASE_PAYLOAD_MTU (pay);
624
625   gst_buffer_map (buffer, &map, GST_MAP_READ);
626   data = map.data;
627   size = map.size;
628   timestamp = GST_BUFFER_TIMESTAMP (buffer);
629   offset = 0;
630
631   GST_LOG_OBJECT (pay, "got buffer size %" G_GSIZE_FORMAT
632       " , timestamp %" GST_TIME_FORMAT, size, GST_TIME_ARGS (timestamp));
633
634   /* parse the jpeg header for 'start of scan' and read quant tables if needed */
635   sos_found = FALSE;
636   dqt_found = FALSE;
637   sof_found = FALSE;
638   dri_found = FALSE;
639
640   while (!sos_found && (offset < size)) {
641     GST_LOG_OBJECT (pay, "checking from offset %u", offset);
642     switch (gst_rtp_jpeg_pay_scan_marker (data, size, &offset)) {
643       case JPEG_MARKER_JFIF:
644       case JPEG_MARKER_CMT:
645       case JPEG_MARKER_DHT:
646       case JPEG_MARKER_H264:
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 >= G_N_ELEMENTS (tables))
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   }
738
739   bytes_left = sizeof (jpeg_header) + quant_data_size + size;
740
741   if (dri_found)
742     bytes_left += sizeof (restart_marker_header);
743
744   frame_done = FALSE;
745   do {
746     GstBuffer *outbuf;
747     guint8 *payload;
748     guint payload_size = (bytes_left < mtu ? bytes_left : mtu);
749     guint header_size;
750     GstBuffer *paybuf;
751     GstRTPBuffer rtp = { NULL };
752
753     header_size = sizeof (jpeg_header) + quant_data_size;
754     if (dri_found)
755       header_size += sizeof (restart_marker_header);
756
757     outbuf = gst_rtp_buffer_new_allocate (header_size, 0, 0);
758
759     gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp);
760
761     if (payload_size == bytes_left) {
762       GST_LOG_OBJECT (pay, "last packet of frame");
763       frame_done = TRUE;
764       gst_rtp_buffer_set_marker (&rtp, 1);
765     }
766
767     payload = gst_rtp_buffer_get_payload (&rtp);
768
769     /* update offset */
770 #if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
771     jpeg_header.offset = ((offset & 0x0000FF) << 16) |
772         ((offset & 0xFF0000) >> 16) | (offset & 0x00FF00);
773 #else
774     jpeg_header.offset = offset;
775 #endif
776     memcpy (payload, &jpeg_header, sizeof (jpeg_header));
777     payload += sizeof (jpeg_header);
778     payload_size -= sizeof (jpeg_header);
779
780     if (dri_found) {
781       memcpy (payload, &restart_marker_header, sizeof (restart_marker_header));
782       payload += sizeof (restart_marker_header);
783       payload_size -= sizeof (restart_marker_header);
784     }
785
786     /* only send quant table with first packet */
787     if (G_UNLIKELY (quant_data_size > 0)) {
788       memcpy (payload, &quant_header, sizeof (quant_header));
789       payload += sizeof (quant_header);
790
791       /* copy the quant tables for luma and chrominance */
792       for (i = 0; i < 2; i++) {
793         guint qsize;
794         guint qt;
795
796         qt = info[i].qt;
797         qsize = tables[qt].size;
798         memcpy (payload, tables[qt].data, qsize);
799
800         GST_LOG_OBJECT (pay, "component %d using quant %d, size %d", i, qt,
801             qsize);
802
803         payload += qsize;
804       }
805       payload_size -= quant_data_size;
806       bytes_left -= quant_data_size;
807       quant_data_size = 0;
808     }
809     GST_LOG_OBJECT (pay, "sending payload size %d", payload_size);
810     gst_rtp_buffer_unmap (&rtp);
811
812     /* create a new buf to hold the payload */
813     paybuf = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_MEMORY,
814         jpeg_header_size + offset, payload_size);
815
816     /* join memory parts */
817     outbuf = gst_buffer_join (outbuf, paybuf);
818
819     GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
820
821     if (pay->buffer_list) {
822       /* and add to list */
823       gst_buffer_list_insert (list, -1, outbuf);
824     } else {
825       ret = gst_rtp_base_payload_push (basepayload, outbuf);
826       if (ret != GST_FLOW_OK)
827         break;
828     }
829
830     bytes_left -= payload_size;
831     offset += payload_size;
832     data += payload_size;
833   }
834   while (!frame_done);
835
836   if (pay->buffer_list) {
837     /* push the whole buffer list at once */
838     ret = gst_rtp_base_payload_push_list (basepayload, list);
839   }
840
841   gst_buffer_unmap (buffer, &map);
842   gst_buffer_unref (buffer);
843
844   return ret;
845
846   /* ERRORS */
847 unsupported_jpeg:
848   {
849     GST_ELEMENT_ERROR (pay, STREAM, FORMAT, ("Unsupported JPEG"), (NULL));
850     gst_buffer_unmap (buffer, &map);
851     gst_buffer_unref (buffer);
852     return GST_FLOW_NOT_SUPPORTED;
853   }
854 no_dimension:
855   {
856     GST_ELEMENT_ERROR (pay, STREAM, FORMAT, ("No size given"), (NULL));
857     gst_buffer_unmap (buffer, &map);
858     gst_buffer_unref (buffer);
859     return GST_FLOW_NOT_NEGOTIATED;
860   }
861 invalid_format:
862   {
863     /* error was posted */
864     gst_buffer_unmap (buffer, &map);
865     gst_buffer_unref (buffer);
866     return GST_FLOW_ERROR;
867   }
868 invalid_quant:
869   {
870     GST_ELEMENT_ERROR (pay, STREAM, FORMAT, ("Invalid quant tables"), (NULL));
871     gst_buffer_unmap (buffer, &map);
872     gst_buffer_unref (buffer);
873     return GST_FLOW_ERROR;
874   }
875 }
876
877 static void
878 gst_rtp_jpeg_pay_set_property (GObject * object, guint prop_id,
879     const GValue * value, GParamSpec * pspec)
880 {
881   GstRtpJPEGPay *rtpjpegpay;
882
883   rtpjpegpay = GST_RTP_JPEG_PAY (object);
884
885   switch (prop_id) {
886     case PROP_JPEG_QUALITY:
887       rtpjpegpay->quality = g_value_get_int (value);
888       GST_DEBUG_OBJECT (object, "quality = %d", rtpjpegpay->quality);
889       break;
890     case PROP_JPEG_TYPE:
891       rtpjpegpay->type = g_value_get_int (value);
892       GST_DEBUG_OBJECT (object, "type = %d", rtpjpegpay->type);
893       break;
894     case PROP_BUFFER_LIST:
895       rtpjpegpay->buffer_list = g_value_get_boolean (value);
896       GST_DEBUG_OBJECT (object, "buffer_list = %d", rtpjpegpay->buffer_list);
897       break;
898     default:
899       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
900       break;
901   }
902 }
903
904 static void
905 gst_rtp_jpeg_pay_get_property (GObject * object, guint prop_id,
906     GValue * value, GParamSpec * pspec)
907 {
908   GstRtpJPEGPay *rtpjpegpay;
909
910   rtpjpegpay = GST_RTP_JPEG_PAY (object);
911
912   switch (prop_id) {
913     case PROP_JPEG_QUALITY:
914       g_value_set_int (value, rtpjpegpay->quality);
915       break;
916     case PROP_JPEG_TYPE:
917       g_value_set_int (value, rtpjpegpay->type);
918       break;
919     case PROP_BUFFER_LIST:
920       g_value_set_boolean (value, rtpjpegpay->buffer_list);
921       break;
922     default:
923       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
924       break;
925   }
926 }
927
928 gboolean
929 gst_rtp_jpeg_pay_plugin_init (GstPlugin * plugin)
930 {
931   return gst_element_register (plugin, "rtpjpegpay", GST_RANK_SECONDARY,
932       GST_TYPE_RTP_JPEG_PAY);
933 }