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>
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.
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.
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.
23 * SECTION:element-rtpjpegpay
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
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.
33 * The payloader assumes that correct width and height is found in the caps.
41 #include <gst/rtp/gstrtpbuffer.h>
42 #include <gst/video/video.h>
44 #include "gstrtpjpegpay.h"
45 #include "gstrtputils.h"
46 #include "gstbuffermemory.h"
48 static GstStaticPadTemplate gst_rtp_jpeg_pay_sink_template =
49 GST_STATIC_PAD_TEMPLATE ("sink",
52 GST_STATIC_CAPS ("image/jpeg; " "video/x-jpeg")
55 static GstStaticPadTemplate gst_rtp_jpeg_pay_src_template =
56 GST_STATIC_PAD_TEMPLATE ("src",
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 ]")
73 GST_DEBUG_CATEGORY_STATIC (rtpjpegpay_debug);
74 #define GST_CAT_DEFAULT (rtpjpegpay_debug)
79 * Prefix length in the header before the quantization tables:
80 * Two size bytes and one byte for precision
82 #define QUANT_PREFIX_LEN 3
85 typedef enum _RtpJpegMarker 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
101 * Identifiers for markers in JPEG header
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
123 #define DEFAULT_JPEG_QUANT 255
125 #define DEFAULT_JPEG_QUALITY 255
126 #define DEFAULT_JPEG_TYPE 1
139 Q_TABLE_MAX /* only support for two tables at the moment */
142 typedef struct _RtpJpegHeader RtpJpegHeader;
146 * @type_spec: type specific
147 * @offset: fragment offset
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
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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
161 struct _RtpJpegHeader
174 * @precision: specify size of quantization tables
175 * @length: length of quantization data
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 |
184 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
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"
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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
212 * The restart marker header is implemented according to the following
213 * methodology specified in section 3.1.7 of rfc2435.txt.
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."
224 guint16 restart_interval;
225 guint16 restart_count;
226 } RtpRestartMarkerHeader;
235 /* FIXME: restart marker header currently unsupported */
237 static void gst_rtp_jpeg_pay_set_property (GObject * object, guint prop_id,
238 const GValue * value, GParamSpec * pspec);
240 static void gst_rtp_jpeg_pay_get_property (GObject * object, guint prop_id,
241 GValue * value, GParamSpec * pspec);
243 static gboolean gst_rtp_jpeg_pay_setcaps (GstRTPBasePayload * basepayload,
246 static GstFlowReturn gst_rtp_jpeg_pay_handle_buffer (GstRTPBasePayload * pad,
249 #define gst_rtp_jpeg_pay_parent_class parent_class
250 G_DEFINE_TYPE (GstRtpJPEGPay, gst_rtp_jpeg_pay, GST_TYPE_RTP_BASE_PAYLOAD);
253 gst_rtp_jpeg_pay_class_init (GstRtpJPEGPayClass * klass)
255 GObjectClass *gobject_class;
256 GstElementClass *gstelement_class;
257 GstRTPBasePayloadClass *gstrtpbasepayload_class;
259 gobject_class = (GObjectClass *) klass;
260 gstelement_class = (GstElementClass *) klass;
261 gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass;
263 gobject_class->set_property = gst_rtp_jpeg_pay_set_property;
264 gobject_class->get_property = gst_rtp_jpeg_pay_get_property;
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);
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>");
276 gstrtpbasepayload_class->set_caps = gst_rtp_jpeg_pay_setcaps;
277 gstrtpbasepayload_class->handle_buffer = gst_rtp_jpeg_pay_handle_buffer;
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));
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));
289 GST_DEBUG_CATEGORY_INIT (rtpjpegpay_debug, "rtpjpegpay", 0,
290 "Motion JPEG RTP Payloader");
294 gst_rtp_jpeg_pay_init (GstRtpJPEGPay * pay)
296 pay->quality = DEFAULT_JPEG_QUALITY;
297 pay->quant = DEFAULT_JPEG_QUANT;
298 pay->type = DEFAULT_JPEG_TYPE;
302 GST_RTP_BASE_PAYLOAD_PT (pay) = GST_RTP_PAYLOAD_JPEG;
306 gst_rtp_jpeg_pay_setcaps (GstRTPBasePayload * basepayload, GstCaps * caps)
308 GstStructure *caps_structure = gst_caps_get_structure (caps, 0);
311 gint width = -1, height = -1;
316 pay = GST_RTP_JPEG_PAY (basepayload);
318 /* these properties are mandatory, but they might be adjusted by the SOF, if there
320 if (!gst_structure_get_int (caps_structure, "height", &height) || height <= 0) {
321 goto invalid_dimension;
324 if (!gst_structure_get_int (caps_structure, "width", &width) || width <= 0) {
325 goto invalid_dimension;
328 if (gst_structure_get_fraction (caps_structure, "framerate", &num, &denom) &&
329 (num < 0 || denom <= 0)) {
330 goto invalid_framerate;
333 if (height > 2040 || width > 2040) {
337 pay->height = GST_ROUND_UP_8 (height) / 8;
338 pay->width = GST_ROUND_UP_8 (width) / 8;
341 gst_rtp_base_payload_set_options (basepayload, "video",
342 basepayload->pt != GST_RTP_PAYLOAD_JPEG, "JPEG", 90000);
346 gst_util_fraction_to_double (num, denom, &framerate);
347 rate = g_strdup_printf ("%f", framerate);
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);
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);
366 res = gst_rtp_base_payload_set_outcaps (basepayload, NULL);
377 GST_ERROR_OBJECT (pay, "Invalid width/height from caps");
382 GST_ERROR_OBJECT (pay, "Invalid framerate from caps");
388 * get uint16 value from current position in mapped memory.
389 * the memory offset will be increased with 2.
392 parse_mem_inc_offset_guint16 (GstBufferMemoryMap * memory)
396 g_return_val_if_fail (memory->total_size > (memory->offset + 1), 0);
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);
407 * get uint8 value from current position in mapped memory.
408 * the memory offset will be increased with 1.
411 parse_mem_inc_offset_guint8 (GstBufferMemoryMap * memory)
415 g_return_val_if_fail (memory->total_size > memory->offset, 0);
417 data = (*memory->data);
418 gst_buffer_memory_advance_bytes (memory, 1);
424 gst_rtp_jpeg_pay_read_quant_table (GstBufferMemoryMap * memory,
425 RtpQuantTable tables[])
427 guint quant_size, tab_size;
431 if (memory->total_size <= (memory->offset + 1))
434 quant_size = parse_mem_inc_offset_guint16 (memory);
436 goto small_quant_size;
438 /* clamp to available data */
439 if (memory->offset + quant_size > memory->total_size)
440 quant_size = memory->total_size - memory->offset;
444 while (quant_size > 0) {
446 /* not enough to read the id */
447 if (memory->offset + 1 > memory->total_size)
450 data = parse_mem_inc_offset_guint8 (memory);
453 /* invalid id received - corrupt data */
456 prec = (data & 0xf0) >> 4;
462 /* there is not enough for the table */
463 if (quant_size < tab_size + 1)
466 GST_LOG ("read quant table %d, tab_size %d, prec %02x", id, tab_size, prec);
468 tables[id].size = tab_size;
469 tables[id].data = memory->data;
471 quant_size -= (tab_size + 1);
472 if (!gst_buffer_memory_advance_bytes (memory, tab_size)) {
482 GST_WARNING ("not enough data");
487 GST_WARNING ("quant_size too small (%u < 2)", quant_size);
492 GST_WARNING ("invalid id");
497 GST_WARNING ("not enough data for table (%u < %u)", quant_size,
504 gst_rtp_jpeg_pay_read_sof (GstRtpJPEGPay * pay, GstBufferMemoryMap * memory,
505 CompInfo info[], RtpQuantTable tables[], gulong tables_elements)
508 guint width, height, infolen;
512 off = memory->offset;
514 /* we need at least 17 bytes for the SOF */
515 if (off + 17 > memory->total_size)
518 sof_size = parse_mem_inc_offset_guint16 (memory);
522 /* precision should be 8 */
523 if (parse_mem_inc_offset_guint8 (memory) != 8)
526 /* read dimensions */
527 height = parse_mem_inc_offset_guint16 (memory);
528 width = parse_mem_inc_offset_guint16 (memory);
530 GST_LOG_OBJECT (pay, "got dimensions %ux%u", height, width);
533 goto invalid_dimension;
539 goto invalid_dimension;
545 if (height == 0 || width == 0) {
549 pay->height = GST_ROUND_UP_8 (height) / 8;
550 pay->width = GST_ROUND_UP_8 (width) / 8;
553 /* we only support 3 components */
554 if (parse_mem_inc_offset_guint8 (memory) != 3)
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,
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))
568 info[j] = info[j - 1];
574 /* see that the components are supported */
575 if (info[0].samp == 0x21)
577 else if (info[0].samp == 0x22)
582 if (!(info[1].samp == 0x11))
585 if (!(info[2].samp == 0x11))
593 GST_ELEMENT_WARNING (pay, STREAM, FORMAT,
594 ("Wrong size %u (needed %u).", (guint) memory->total_size, off + 17),
600 GST_ELEMENT_WARNING (pay, STREAM, FORMAT,
601 ("Wrong SOF length %u.", sof_size), (NULL));
606 GST_ELEMENT_WARNING (pay, STREAM, FORMAT,
607 ("Wrong precision, expecting 8."), (NULL));
612 GST_ELEMENT_WARNING (pay, STREAM, FORMAT,
613 ("Wrong dimension, size %ux%u", width, height), (NULL));
618 GST_ELEMENT_WARNING (pay, STREAM, FORMAT,
619 ("Wrong number of components"), (NULL));
624 GST_ELEMENT_WARNING (pay, STREAM, FORMAT, ("Invalid component"), (NULL));
630 gst_rtp_jpeg_pay_read_dri (GstRtpJPEGPay * pay, GstBufferMemoryMap * memory,
631 RtpRestartMarkerHeader * dri)
633 guint dri_size, restart_interval;
635 /* we need at least 4 bytes for the DRI */
636 if (memory->offset + 4 > memory->total_size)
639 dri_size = parse_mem_inc_offset_guint16 (memory);
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)) {
650 return dri->restart_interval > 0;
654 GST_WARNING ("not enough data for DRI");
659 GST_WARNING ("DRI size too small (%u)", dri_size);
660 /* offset got incremented by two when dri_size was parsed. */
662 gst_buffer_memory_advance_bytes (memory, dri_size - 2);
668 gst_rtp_jpeg_pay_skipping_marker (GstBufferMemoryMap * memory)
672 if (G_UNLIKELY (((memory->offset + 1) >= memory->total_size))) {
675 skip = parse_mem_inc_offset_guint16 (memory);
677 if (G_UNLIKELY (((skip - 2 + memory->offset) > memory->total_size))) {
681 gst_buffer_memory_advance_bytes (memory, skip - 2);
687 GST_WARNING ("not enough data");
692 gst_rtp_jpeg_pay_scan_marker (GstBufferMemoryMap * memory)
694 guint8 marker = parse_mem_inc_offset_guint8 (memory);
696 while (marker != JPEG_MARKER && ((memory->offset) < memory->total_size)) {
697 marker = parse_mem_inc_offset_guint8 (memory);
700 if (G_UNLIKELY ((memory->offset) >= memory->total_size)) {
701 GST_LOG ("found EOI marker");
702 return JPEG_MARKER_EOI;
704 marker = parse_mem_inc_offset_guint8 (memory);
709 #define RTP_HEADER_LEN 12
712 gst_rtp_jpeg_pay_handle_buffer (GstRTPBasePayload * basepayload,
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;
726 guint jpeg_header_size = 0;
729 gboolean sos_found, sof_found, dqt_found, dri_found;
731 GstBufferList *list = NULL;
733 GstBufferMemoryMap memory;
735 pay = GST_RTP_JPEG_PAY (basepayload);
736 mtu = GST_RTP_BASE_PAYLOAD_MTU (pay);
738 gst_buffer_memory_map (buffer, &memory);
740 timestamp = GST_BUFFER_PTS (buffer);
741 discont = GST_BUFFER_IS_DISCONT (buffer);
743 GST_LOG_OBJECT (pay, "got buffer size %" G_GSIZE_FORMAT
744 " , timestamp %" GST_TIME_FORMAT, memory.total_size,
745 GST_TIME_ARGS (timestamp));
747 /* parse the jpeg header for 'start of scan' and read quant tables if needed */
753 while (!sos_found && (memory.offset < memory.total_size)) {
756 GST_LOG_OBJECT (pay, "checking from offset %u", memory.offset);
757 marker = gst_rtp_jpeg_pay_scan_marker (&memory);
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);
766 case JPEG_MARKER_SOF:
767 if (!gst_rtp_jpeg_pay_read_sof (pay, &memory, info, tables,
768 G_N_ELEMENTS (tables)))
772 case JPEG_MARKER_DQT:
773 GST_LOG ("DQT found");
774 gst_rtp_jpeg_pay_read_quant_table (&memory, tables);
777 case JPEG_MARKER_SOS:
779 GST_LOG_OBJECT (pay, "SOS found");
781 memory.offset + parse_mem_inc_offset_guint16 (&memory);
783 case JPEG_MARKER_EOI:
784 GST_WARNING_OBJECT (pay, "EOI reached before SOS!");
786 case JPEG_MARKER_SOI:
787 GST_LOG_OBJECT (pay, "SOI found");
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))
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);
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);
807 if (!dqt_found || !sof_found)
808 goto unsupported_jpeg;
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) {
816 GST_LOG_OBJECT (pay, "header size %u", jpeg_header_size);
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;
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++) {
843 if (qt >= G_N_ELEMENTS (tables))
846 qsize = tables[qt].size;
850 quant_header.precision |= (qsize == 64 ? 0 : (1 << i));
851 quant_data_size += qsize;
853 quant_header.length = g_htons (quant_data_size);
854 quant_data_size += sizeof (quant_header);
857 GST_LOG_OBJECT (pay, "quant_data size %u", quant_data_size);
860 sizeof (jpeg_header) + quant_data_size + memory.total_size -
864 bytes_left += sizeof (restart_marker_header);
866 max_payload_size = mtu - (RTP_HEADER_LEN + sizeof (jpeg_header));
867 list = gst_buffer_list_new_sized ((bytes_left / max_payload_size) + 1);
876 GstRTPBuffer rtp = { NULL };
877 guint rtp_header_size = gst_rtp_buffer_calc_header_len (0);
879 /* The available room is the packet MTU, minus the RTP header length. */
881 (bytes_left < (mtu - rtp_header_size) ? bytes_left :
882 (mtu - rtp_header_size));
884 header_size = sizeof (jpeg_header) + quant_data_size;
886 header_size += sizeof (restart_marker_header);
888 outbuf = gst_rtp_buffer_new_allocate (header_size, 0, 0);
890 gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp);
892 if (payload_size == bytes_left) {
893 GST_LOG_OBJECT (pay, "last packet of frame");
895 gst_rtp_buffer_set_marker (&rtp, 1);
898 payload = gst_rtp_buffer_get_payload (&rtp);
901 #if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
902 jpeg_header.offset = ((offset & 0x0000FF) << 16) |
903 ((offset & 0xFF0000) >> 16) | (offset & 0x00FF00);
905 jpeg_header.offset = offset;
907 memcpy (payload, &jpeg_header, sizeof (jpeg_header));
908 payload += sizeof (jpeg_header);
909 payload_size -= sizeof (jpeg_header);
912 memcpy (payload, &restart_marker_header, sizeof (restart_marker_header));
913 payload += sizeof (restart_marker_header);
914 payload_size -= sizeof (restart_marker_header);
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);
922 /* copy the quant tables for luma and chrominance */
923 for (i = 0; i < 2; i++) {
928 qsize = tables[qt].size;
929 memcpy (payload, tables[qt].data, qsize);
931 GST_LOG_OBJECT (pay, "component %d using quant %d, size %d", i, qt,
936 payload_size -= quant_data_size;
937 bytes_left -= quant_data_size;
940 GST_LOG_OBJECT (pay, "sending payload size %d", payload_size);
941 gst_rtp_buffer_unmap (&rtp);
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);
947 /* join memory parts */
948 gst_rtp_copy_video_meta (pay, outbuf, paybuf);
949 outbuf = gst_buffer_append (outbuf, paybuf);
951 GST_BUFFER_PTS (outbuf) = timestamp;
954 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
955 /* Only the first outputted buffer has the DISCONT flag */
959 /* and add to list */
960 gst_buffer_list_insert (list, -1, outbuf);
962 bytes_left -= payload_size;
963 offset += payload_size;
966 /* push the whole buffer list at once */
967 ret = gst_rtp_base_payload_push_list (basepayload, list);
969 gst_buffer_memory_unmap (&memory);
970 gst_buffer_unref (buffer);
977 GST_ELEMENT_WARNING (pay, STREAM, FORMAT, ("Unsupported JPEG"), (NULL));
978 gst_buffer_memory_unmap (&memory);
979 gst_buffer_unref (buffer);
984 GST_ELEMENT_WARNING (pay, STREAM, FORMAT, ("No size given"), (NULL));
985 gst_buffer_memory_unmap (&memory);
986 gst_buffer_unref (buffer);
991 /* error was posted */
992 gst_buffer_memory_unmap (&memory);
993 gst_buffer_unref (buffer);
998 GST_ELEMENT_WARNING (pay, STREAM, FORMAT, ("Invalid quant tables"), (NULL));
999 gst_buffer_memory_unmap (&memory);
1000 gst_buffer_unref (buffer);
1006 gst_rtp_jpeg_pay_set_property (GObject * object, guint prop_id,
1007 const GValue * value, GParamSpec * pspec)
1009 GstRtpJPEGPay *rtpjpegpay;
1011 rtpjpegpay = GST_RTP_JPEG_PAY (object);
1014 case PROP_JPEG_QUALITY:
1015 rtpjpegpay->quality = g_value_get_int (value);
1016 GST_DEBUG_OBJECT (object, "quality = %d", rtpjpegpay->quality);
1018 case PROP_JPEG_TYPE:
1019 rtpjpegpay->type = g_value_get_int (value);
1020 GST_DEBUG_OBJECT (object, "type = %d", rtpjpegpay->type);
1023 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1029 gst_rtp_jpeg_pay_get_property (GObject * object, guint prop_id,
1030 GValue * value, GParamSpec * pspec)
1032 GstRtpJPEGPay *rtpjpegpay;
1034 rtpjpegpay = GST_RTP_JPEG_PAY (object);
1037 case PROP_JPEG_QUALITY:
1038 g_value_set_int (value, rtpjpegpay->quality);
1040 case PROP_JPEG_TYPE:
1041 g_value_set_int (value, rtpjpegpay->type);
1044 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1050 gst_rtp_jpeg_pay_plugin_init (GstPlugin * plugin)
1052 return gst_element_register (plugin, "rtpjpegpay", GST_RANK_SECONDARY,
1053 GST_TYPE_RTP_JPEG_PAY);