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