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