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