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