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