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
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
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.
32 * The payloader assumes that correct width and height is found in the caps.
40 #include <gst/rtp/gstrtpbuffer.h>
42 #include "gstrtpjpegpay.h"
44 static GstStaticPadTemplate gst_rtp_jpeg_pay_sink_template =
45 GST_STATIC_PAD_TEMPLATE ("sink",
48 GST_STATIC_CAPS ("image/jpeg; " "video/x-jpeg")
51 static GstStaticPadTemplate gst_rtp_jpeg_pay_src_template =
52 GST_STATIC_PAD_TEMPLATE ("src",
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 ]")
64 GST_DEBUG_CATEGORY_STATIC (rtpjpegpay_debug);
65 #define GST_CAT_DEFAULT (rtpjpegpay_debug)
70 * Prefix length in the header before the quantization tables:
71 * Two size bytes and one byte for precision
73 #define QUANT_PREFIX_LEN 3
76 typedef enum _RtpJpegMarker 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
92 * Identifers for markers in JPEG header
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
109 #define DEFAULT_JPEG_QUANT 255
111 #define DEFAULT_JPEG_QUALITY 255
112 #define DEFAULT_JPEG_TYPE 1
126 Q_TABLE_MAX /* only support for two tables at the moment */
129 typedef struct _RtpJpegHeader RtpJpegHeader;
133 * @type_spec: type specific
134 * @offset: fragment offset
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
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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
148 struct _RtpJpegHeader
161 * @precision: specify size of quantization tables
162 * @length: length of quantization data
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 |
171 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
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"
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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
199 * The restart marker header is implemented according to the following
200 * methodology specified in section 3.1.7 of rfc2435.txt.
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."
211 guint16 restart_interval;
212 guint16 restart_count;
213 } RtpRestartMarkerHeader;
222 /* FIXME: restart marker header currently unsupported */
224 static void gst_rtp_jpeg_pay_set_property (GObject * object, guint prop_id,
225 const GValue * value, GParamSpec * pspec);
227 static void gst_rtp_jpeg_pay_get_property (GObject * object, guint prop_id,
228 GValue * value, GParamSpec * pspec);
230 static gboolean gst_rtp_jpeg_pay_setcaps (GstRTPBasePayload * basepayload,
233 static GstFlowReturn gst_rtp_jpeg_pay_handle_buffer (GstRTPBasePayload * pad,
236 #define gst_rtp_jpeg_pay_parent_class parent_class
237 G_DEFINE_TYPE (GstRtpJPEGPay, gst_rtp_jpeg_pay, GST_TYPE_RTP_BASE_PAYLOAD);
240 gst_rtp_jpeg_pay_class_init (GstRtpJPEGPayClass * klass)
242 GObjectClass *gobject_class;
243 GstElementClass *gstelement_class;
244 GstRTPBasePayloadClass *gstrtpbasepayload_class;
246 gobject_class = (GObjectClass *) klass;
247 gstelement_class = (GstElementClass *) klass;
248 gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass;
250 gobject_class->set_property = gst_rtp_jpeg_pay_set_property;
251 gobject_class->get_property = gst_rtp_jpeg_pay_get_property;
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));
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>");
263 gstrtpbasepayload_class->set_caps = gst_rtp_jpeg_pay_setcaps;
264 gstrtpbasepayload_class->handle_buffer = gst_rtp_jpeg_pay_handle_buffer;
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));
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));
276 GST_DEBUG_CATEGORY_INIT (rtpjpegpay_debug, "rtpjpegpay", 0,
277 "Motion JPEG RTP Payloader");
281 gst_rtp_jpeg_pay_init (GstRtpJPEGPay * pay)
283 pay->quality = DEFAULT_JPEG_QUALITY;
284 pay->quant = DEFAULT_JPEG_QUANT;
285 pay->type = DEFAULT_JPEG_TYPE;
291 gst_rtp_jpeg_pay_setcaps (GstRTPBasePayload * basepayload, GstCaps * caps)
293 GstStructure *caps_structure = gst_caps_get_structure (caps, 0);
296 gint width = -1, height = -1;
302 pay = GST_RTP_JPEG_PAY (basepayload);
304 /* these properties are mandatory, but they might be adjusted by the SOF, if there
306 if (!gst_structure_get_int (caps_structure, "height", &height) || height <= 0) {
307 goto invalid_dimension;
310 if (!gst_structure_get_int (caps_structure, "width", &width) || width <= 0) {
311 goto invalid_dimension;
314 if (gst_structure_get_fraction (caps_structure, "framerate", &num, &denom) &&
315 (num < 0 || denom <= 0)) {
316 goto invalid_framerate;
319 if (height > 2040 || width > 2040) {
323 pay->height = GST_ROUND_UP_8 (height) / 8;
324 pay->width = GST_ROUND_UP_8 (width) / 8;
327 gst_rtp_base_payload_set_options (basepayload, "video", TRUE, "JPEG", 90000);
332 gst_util_fraction_to_double (num, denom, &framerate);
333 rate = g_strdup_printf("%f", framerate);
336 size = g_strdup_printf("%d-%d", width, height);
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);
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);
355 res = gst_rtp_base_payload_set_outcaps (basepayload, "a-framesize",
356 G_TYPE_STRING, size, NULL);
370 GST_ERROR_OBJECT (pay, "Invalid width/height from caps");
375 GST_ERROR_OBJECT (pay, "Invalid framerate from caps");
381 gst_rtp_jpeg_pay_header_size (const guint8 * data, guint offset)
383 return data[offset] << 8 | data[offset + 1];
387 gst_rtp_jpeg_pay_read_quant_table (const guint8 * data, guint size,
388 guint offset, RtpQuantTable tables[])
390 guint quant_size, tab_size;
394 if (offset + 2 > size)
397 quant_size = gst_rtp_jpeg_pay_header_size (data, offset);
399 goto small_quant_size;
401 /* clamp to available data */
402 if (offset + quant_size > size)
403 quant_size = size - offset;
408 while (quant_size > 0) {
409 /* not enough to read the id */
410 if (offset + 1 > size)
413 id = data[offset] & 0x0f;
415 /* invalid id received - corrupt data */
418 prec = (data[offset] & 0xf0) >> 4;
424 /* there is not enough for the table */
425 if (quant_size < tab_size + 1)
428 GST_LOG ("read quant table %d, tab_size %d, prec %02x", id, tab_size, prec);
430 tables[id].size = tab_size;
431 tables[id].data = &data[offset + 1];
434 quant_size -= tab_size;
438 return offset + quant_size;
443 GST_WARNING ("not enough data");
448 GST_WARNING ("quant_size too small (%u < 2)", quant_size);
453 GST_WARNING ("invalid id");
458 GST_WARNING ("not enough data for table (%u < %u)", quant_size,
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)
470 guint width, height, infolen;
476 /* we need at least 17 bytes for the SOF */
480 sof_size = gst_rtp_jpeg_pay_header_size (data, off);
489 /* precision should be 8 */
490 if (data[off++] != 8)
493 /* read dimensions */
494 height = data[off] << 8 | data[off + 1];
495 width = data[off + 2] << 8 | data[off + 3];
498 GST_LOG_OBJECT (pay, "got dimensions %ux%u", height, width);
501 goto invalid_dimension;
507 goto invalid_dimension;
513 if (height == 0 || width == 0) {
517 pay->height = GST_ROUND_UP_8 (height) / 8;
518 pay->width = GST_ROUND_UP_8 (width) / 8;
521 /* we only support 3 components */
522 if (data[off++] != 3)
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,
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))
536 info[j] = info[j - 1];
542 /* see that the components are supported */
543 if (info[0].samp == 0x21)
545 else if (info[0].samp == 0x22)
550 if (!(info[1].samp == 0x11))
553 if (!(info[2].samp == 0x11))
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))
576 GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
577 ("Wrong size %u (needed %u).", size, off + 17), (NULL));
582 GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
583 ("Wrong SOF length %u.", sof_size), (NULL));
588 GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
589 ("Wrong precision, expecting 8."), (NULL));
594 GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
595 ("Wrong dimension, size %ux%u", width, height), (NULL));
600 GST_ELEMENT_ERROR (pay, STREAM, FORMAT,
601 ("Wrong number of components"), (NULL));
606 GST_ELEMENT_ERROR (pay, STREAM, FORMAT, ("Invalid component"), (NULL));
612 gst_rtp_jpeg_pay_read_dri (GstRtpJPEGPay * pay, const guint8 * data,
613 guint size, guint * offset, RtpRestartMarkerHeader * dri)
619 /* we need at least 4 bytes for the DRI */
623 dri_size = gst_rtp_jpeg_pay_header_size (data, off);
630 dri->restart_interval = g_htons ((data[off] << 8) | (data[off + 1]));
631 dri->restart_count = g_htons (0xFFFF);
633 return dri->restart_interval > 0;
637 GST_WARNING ("not enough data for DRI");
643 GST_WARNING ("DRI size too small (%u)", dri_size);
650 gst_rtp_jpeg_pay_scan_marker (const guint8 * data, guint size, guint * offset)
652 while ((data[(*offset)++] != JPEG_MARKER) && ((*offset) < size));
654 if (G_UNLIKELY ((*offset) >= size)) {
655 GST_LOG ("found EOI marker");
656 return JPEG_MARKER_EOI;
660 marker = data[*offset];
661 GST_LOG ("found 0x%02x marker at offset %u", marker, *offset);
668 gst_rtp_jpeg_pay_handle_buffer (GstRTPBasePayload * basepayload,
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;
685 guint jpeg_header_size = 0;
688 gboolean sos_found, sof_found, dqt_found, dri_found;
690 GstBufferList *list = NULL;
692 pay = GST_RTP_JPEG_PAY (basepayload);
693 mtu = GST_RTP_BASE_PAYLOAD_MTU (pay);
695 gst_buffer_map (buffer, &map, GST_MAP_READ);
698 timestamp = GST_BUFFER_TIMESTAMP (buffer);
701 GST_LOG_OBJECT (pay, "got buffer size %" G_GSIZE_FORMAT
702 " , timestamp %" GST_TIME_FORMAT, size, GST_TIME_ARGS (timestamp));
704 /* parse the jpeg header for 'start of scan' and read quant tables if needed */
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);
720 case JPEG_MARKER_SOF:
721 if (!gst_rtp_jpeg_pay_read_sof (pay, data, size, &offset, info, tables,
722 G_N_ELEMENTS (tables)))
726 case JPEG_MARKER_DQT:
727 GST_LOG ("DQT found");
728 offset = gst_rtp_jpeg_pay_read_quant_table (data, size, offset, tables);
731 case JPEG_MARKER_SOS:
733 GST_LOG_OBJECT (pay, "SOS found");
734 jpeg_header_size = offset + gst_rtp_jpeg_pay_header_size (data, offset);
736 case JPEG_MARKER_EOI:
737 GST_WARNING_OBJECT (pay, "EOI reached before SOS!");
739 case JPEG_MARKER_SOI:
740 GST_LOG_OBJECT (pay, "SOI found");
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))
752 if (!dqt_found || !sof_found)
753 goto unsupported_jpeg;
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) {
761 GST_LOG_OBJECT (pay, "header size %u", jpeg_header_size);
763 size -= jpeg_header_size;
764 data += jpeg_header_size;
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;
777 /* collect the quant headers sizes */
778 quant_header.mbz = 0;
779 quant_header.precision = 0;
780 quant_header.length = 0;
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++) {
791 if (qt >= G_N_ELEMENTS (tables))
794 qsize = tables[qt].size;
798 quant_header.precision |= (qsize == 64 ? 0 : (1 << i));
799 quant_data_size += qsize;
801 quant_header.length = g_htons (quant_data_size);
802 quant_data_size += sizeof (quant_header);
805 GST_LOG_OBJECT (pay, "quant_data size %u", quant_data_size);
807 list = gst_buffer_list_new ();
809 bytes_left = sizeof (jpeg_header) + quant_data_size + size;
812 bytes_left += sizeof (restart_marker_header);
818 guint payload_size = (bytes_left < mtu ? bytes_left : mtu);
821 GstRTPBuffer rtp = { NULL };
823 header_size = sizeof (jpeg_header) + quant_data_size;
825 header_size += sizeof (restart_marker_header);
827 outbuf = gst_rtp_buffer_new_allocate (header_size, 0, 0);
829 gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp);
831 if (payload_size == bytes_left) {
832 GST_LOG_OBJECT (pay, "last packet of frame");
834 gst_rtp_buffer_set_marker (&rtp, 1);
837 payload = gst_rtp_buffer_get_payload (&rtp);
840 #if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
841 jpeg_header.offset = ((offset & 0x0000FF) << 16) |
842 ((offset & 0xFF0000) >> 16) | (offset & 0x00FF00);
844 jpeg_header.offset = offset;
846 memcpy (payload, &jpeg_header, sizeof (jpeg_header));
847 payload += sizeof (jpeg_header);
848 payload_size -= sizeof (jpeg_header);
851 memcpy (payload, &restart_marker_header, sizeof (restart_marker_header));
852 payload += sizeof (restart_marker_header);
853 payload_size -= sizeof (restart_marker_header);
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);
861 /* copy the quant tables for luma and chrominance */
862 for (i = 0; i < 2; i++) {
867 qsize = tables[qt].size;
868 memcpy (payload, tables[qt].data, qsize);
870 GST_LOG_OBJECT (pay, "component %d using quant %d, size %d", i, qt,
875 payload_size -= quant_data_size;
876 bytes_left -= quant_data_size;
879 GST_LOG_OBJECT (pay, "sending payload size %d", payload_size);
880 gst_rtp_buffer_unmap (&rtp);
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);
886 /* join memory parts */
887 outbuf = gst_buffer_append (outbuf, paybuf);
889 GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
891 /* and add to list */
892 gst_buffer_list_insert (list, -1, outbuf);
894 bytes_left -= payload_size;
895 offset += payload_size;
896 data += payload_size;
900 /* push the whole buffer list at once */
901 ret = gst_rtp_base_payload_push_list (basepayload, list);
903 gst_buffer_unmap (buffer, &map);
904 gst_buffer_unref (buffer);
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;
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;
925 /* error was posted */
926 gst_buffer_unmap (buffer, &map);
927 gst_buffer_unref (buffer);
928 return GST_FLOW_ERROR;
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;
940 gst_rtp_jpeg_pay_set_property (GObject * object, guint prop_id,
941 const GValue * value, GParamSpec * pspec)
943 GstRtpJPEGPay *rtpjpegpay;
945 rtpjpegpay = GST_RTP_JPEG_PAY (object);
948 case PROP_JPEG_QUALITY:
949 rtpjpegpay->quality = g_value_get_int (value);
950 GST_DEBUG_OBJECT (object, "quality = %d", rtpjpegpay->quality);
953 rtpjpegpay->type = g_value_get_int (value);
954 GST_DEBUG_OBJECT (object, "type = %d", rtpjpegpay->type);
957 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
963 gst_rtp_jpeg_pay_get_property (GObject * object, guint prop_id,
964 GValue * value, GParamSpec * pspec)
966 GstRtpJPEGPay *rtpjpegpay;
968 rtpjpegpay = GST_RTP_JPEG_PAY (object);
971 case PROP_JPEG_QUALITY:
972 g_value_set_int (value, rtpjpegpay->quality);
975 g_value_set_int (value, rtpjpegpay->type);
978 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
984 gst_rtp_jpeg_pay_plugin_init (GstPlugin * plugin)
986 return gst_element_register (plugin, "rtpjpegpay", GST_RANK_SECONDARY,
987 GST_TYPE_RTP_JPEG_PAY);