diff options
Diffstat (limited to 'gst/rtp/gstrtpjpegpay.c')
-rw-r--r-- | gst/rtp/gstrtpjpegpay.c | 256 |
1 files changed, 207 insertions, 49 deletions
diff --git a/gst/rtp/gstrtpjpegpay.c b/gst/rtp/gstrtpjpegpay.c index 0de0ac0f..63bcb10c 100644 --- a/gst/rtp/gstrtpjpegpay.c +++ b/gst/rtp/gstrtpjpegpay.c @@ -52,11 +52,7 @@ static GstStaticPadTemplate gst_rtp_jpeg_pay_sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("image/jpeg, " - " height = (int) [0, 2040], " - " width = (int) [0, 2040]; " - "video/x-jpeg, " - " height = (int) [0, 2040], " " width = (int) [0, 2040]") + GST_STATIC_CAPS ("image/jpeg; " "video/x-jpeg") ); static GstStaticPadTemplate gst_rtp_jpeg_pay_src_template = @@ -80,18 +76,6 @@ GST_DEBUG_CATEGORY_STATIC (rtpjpegpay_debug); */ #define QUANT_PREFIX_LEN 3 -/* - * DEFAULT_JPEG_QUALITY: - * - */ -#define DEFAULT_JPEG_QUALITY 255 - -/* - * DEFAULT_JPEG_TYPE: - * - */ -#define DEFAULT_JPEG_TYPE 1 - typedef enum _RtpJpegMarker RtpJpegMarker; /* @@ -121,6 +105,11 @@ enum _RtpJpegMarker JPEG_MARKER_EOI = 0xD9, }; +#define DEFAULT_JPEG_QUANT 255 + +#define DEFAULT_JPEG_QUALITY 255 +#define DEFAULT_JPEG_TYPE 1 + enum { PROP_0, @@ -238,51 +227,61 @@ gst_rtp_jpeg_pay_class_init (GstRtpJPEGPayClass * klass) gstbasertppayload_class->handle_buffer = gst_rtp_jpeg_pay_handle_buffer; g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_JPEG_QUALITY, - g_param_spec_int ("quality", "Quality", "Quality factor on JPEG data", - 0, 255, DEFAULT_JPEG_QUALITY, G_PARAM_READWRITE)); + g_param_spec_int ("quality", "Quality", + "Quality factor on JPEG data (unused)", 0, 255, 255, + G_PARAM_READWRITE)); g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_JPEG_TYPE, - g_param_spec_int ("type", "Type", "JPEG Type", - 0, 255, DEFAULT_JPEG_TYPE, G_PARAM_READWRITE)); + g_param_spec_int ("type", "Type", + "Default JPEG Type, overwritten by SOF when present", 0, 255, + DEFAULT_JPEG_TYPE, G_PARAM_READWRITE)); GST_DEBUG_CATEGORY_INIT (rtpjpegpay_debug, "rtpjpegpay", 0, "Motion JPEG RTP Payloader"); } static void -gst_rtp_jpeg_pay_init (GstRtpJPEGPay * rtpjpegpay, GstRtpJPEGPayClass * klass) +gst_rtp_jpeg_pay_init (GstRtpJPEGPay * pay, GstRtpJPEGPayClass * klass) { - rtpjpegpay->quality = DEFAULT_JPEG_QUALITY; - rtpjpegpay->type = DEFAULT_JPEG_TYPE; + pay->quality = DEFAULT_JPEG_QUALITY; + pay->quant = DEFAULT_JPEG_QUANT; + pay->type = DEFAULT_JPEG_TYPE; } static gboolean gst_rtp_jpeg_pay_setcaps (GstBaseRTPPayload * basepayload, GstCaps * caps) { GstStructure *caps_structure = gst_caps_get_structure (caps, 0); - GstRtpJPEGPay *rtpjpegpay; + GstRtpJPEGPay *pay; + gint width = 0, height = 0; - rtpjpegpay = GST_RTP_JPEG_PAY (basepayload); + pay = GST_RTP_JPEG_PAY (basepayload); - if (!gst_structure_get_int (caps_structure, "height", &rtpjpegpay->height)) { - goto caps_fail; + /* these properties are not mandatory, we can get them from the SOF, if there + * is one. */ + if (gst_structure_get_int (caps_structure, "height", &height)) { + if (height <= 0 || height > 2040) + goto invalid_dimension; } + pay->height = height / 8; - if (!gst_structure_get_int (caps_structure, "width", &rtpjpegpay->width)) { - goto caps_fail; + if (gst_structure_get_int (caps_structure, "width", &width)) { + if (width <= 0 || width > 2040) + goto invalid_dimension; } - - rtpjpegpay->height /= 8; - rtpjpegpay->width /= 8; + pay->width = width / 8; gst_basertppayload_set_options (basepayload, "video", TRUE, "JPEG", 90000); gst_basertppayload_set_outcaps (basepayload, NULL); return TRUE; -caps_fail: - GST_ERROR_OBJECT (rtpjpegpay, "Failed to get width/height from caps"); - return FALSE; + /* ERRORS */ +invalid_dimension: + { + GST_ERROR_OBJECT (pay, "Invalid width/height from caps"); + return FALSE; + } } @@ -296,7 +295,11 @@ static guint gst_rtp_jpeg_pay_read_quant_table (const guint8 * data, guint offset, const guint8 ** quantizer_table, RtpQuantHeader * qtable, guint8 index) { - gint quant_size = gst_rtp_jpeg_pay_header_size (data, offset); + gint quant_size; + + quant_size = gst_rtp_jpeg_pay_header_size (data, offset); + + GST_LOG ("read quant table %d, size %d", index, quant_size); qtable->precision |= (data[offset + 2] & 0x10) ? (1 << index) : 0x00; @@ -309,15 +312,139 @@ gst_rtp_jpeg_pay_read_quant_table (const guint8 * data, guint offset, return quant_size; } +typedef struct +{ + guint8 id; + guint8 samp; + guint8 qt; +} CompInfo; + +static gboolean +gst_rtp_jpeg_pay_read_sof (GstRtpJPEGPay * pay, const guint8 * data, + guint * offset, RtpJpegHeader * header) +{ + guint sof_size; + guint width, height, infolen; + CompInfo elem, info[3], *cptr; + gint i, j; + + sof_size = gst_rtp_jpeg_pay_header_size (data, *offset); + if (sof_size < 17) + goto wrong_length; + + /* skip size */ + *offset += 2; + + /* precision should be 8 */ + if (data[(*offset)++] != 8) + goto bad_precision; + + /* read dimensions */ + height = data[*offset] << 8 | data[*offset + 1]; + width = data[*offset + 2] << 8 | data[*offset + 3]; + *offset += 4; + + GST_LOG_OBJECT (pay, "got dimensions %ux%u", height, width); + + if (height == 0 || height > 2040) + goto invalid_dimension; + if (width == 0 || width > 2040) + goto invalid_dimension; + + pay->height = height / 8; + pay->width = width / 8; + + header->width = pay->width; + header->height = pay->height; + + /* we only support 3 components */ + if (data[(*offset)++] != 3) + goto bad_components; + + infolen = 0; + for (i = 0; i < 3; i++) { + elem.id = data[(*offset)++]; + elem.samp = data[(*offset)++]; + elem.qt = data[(*offset)++]; + GST_LOG_OBJECT (pay, "got comp %d, samp %02x, qt %d", elem.id, elem.samp, + elem.qt); + /* insertion sort from the last element to the first */ + for (j = infolen; j > 1; j--) { + if (G_LIKELY (info[j - 1].id < elem.id)) + break; + info[j] = info[j - 1]; + } + info[j] = elem; + infolen++; + } + + /* see that the components are supported */ + cptr = &info[0]; + if (cptr->samp == 0x21 && cptr->qt == 0) + pay->type = 0; + else if (cptr->samp == 0x22 && cptr->qt == 0) + pay->type = 1; + else + goto invalid_comp; + + header->type = pay->type; + + cptr = &info[1]; + if (!(cptr->samp == 0x11 && cptr->qt == 1)) + goto invalid_comp; + + cptr = &info[2]; + if (!(cptr->samp == 0x11 && cptr->qt == 1)) + goto invalid_comp; + + return TRUE; + + /* ERRORS */ +wrong_length: + { + GST_ELEMENT_ERROR (pay, STREAM, FORMAT, + ("Wrong SOF length %u.", sof_size), (NULL)); + return FALSE; + } +bad_precision: + { + GST_ELEMENT_ERROR (pay, STREAM, FORMAT, + ("Wrong precision, expecting 8."), (NULL)); + return FALSE; + } +invalid_dimension: + { + GST_ELEMENT_ERROR (pay, STREAM, FORMAT, + ("Wrong dimension, size %ux%u", width, height), (NULL)); + return FALSE; + } +bad_components: + { + GST_ELEMENT_ERROR (pay, STREAM, FORMAT, + ("Wrong number of components"), (NULL)); + return FALSE; + } +invalid_comp: + { + GST_ELEMENT_ERROR (pay, STREAM, FORMAT, ("Invalid component"), (NULL)); + return FALSE; + } +} + static RtpJpegMarker gst_rtp_jpeg_pay_scan_marker (const guint8 * data, guint size, guint * offset) { while ((data[(*offset)++] != JPEG_MARKER) && ((*offset) < size)); if (G_UNLIKELY ((*offset) >= size)) { + GST_LOG ("found EOI marker"); return JPEG_MARKER_EOI; } else { - return data[(*offset)++]; + guint8 marker; + + marker = data[(*offset)++]; + GST_LOG ("found %02x marker", marker); + return marker; } } @@ -325,7 +452,7 @@ static GstFlowReturn gst_rtp_jpeg_pay_handle_buffer (GstBaseRTPPayload * basepayload, GstBuffer * buffer) { - GstRtpJPEGPay *rtpjpegpay; + GstRtpJPEGPay *pay; GstClockTime timestamp; GstFlowReturn ret = GST_FLOW_ERROR; RtpJpegHeader jpeg_header; @@ -342,15 +469,16 @@ gst_rtp_jpeg_pay_handle_buffer (GstBaseRTPPayload * basepayload, gboolean frame_done = FALSE; gboolean sos_found = FALSE; - rtpjpegpay = GST_RTP_JPEG_PAY (basepayload); - mtu = GST_BASE_RTP_PAYLOAD_MTU (rtpjpegpay); + pay = GST_RTP_JPEG_PAY (basepayload); + mtu = GST_BASE_RTP_PAYLOAD_MTU (pay); + /* will be overwritten by SOF when present */ jpeg_header.type_spec = 0; jpeg_header.offset = 0; - jpeg_header.type = rtpjpegpay->type; - jpeg_header.q = rtpjpegpay->quality; - jpeg_header.width = rtpjpegpay->width; - jpeg_header.height = rtpjpegpay->height; + jpeg_header.type = pay->type; + jpeg_header.q = pay->quant; + jpeg_header.width = pay->width; + jpeg_header.height = pay->height; size = GST_BUFFER_SIZE (buffer); data = GST_BUFFER_DATA (buffer); @@ -361,11 +489,22 @@ gst_rtp_jpeg_pay_handle_buffer (GstBaseRTPPayload * basepayload, switch (gst_rtp_jpeg_pay_scan_marker (data, size, &offset)) { case JPEG_MARKER_JFIF: case JPEG_MARKER_CMT: + offset += gst_rtp_jpeg_pay_header_size (data, offset); + break; case JPEG_MARKER_SOF: + offset += gst_rtp_jpeg_pay_read_sof (pay, data, &offset, &jpeg_header); + break; case JPEG_MARKER_DHT: - offset += gst_rtp_jpeg_pay_header_size (data, offset); + { + guint len; + + len = gst_rtp_jpeg_pay_header_size (data, offset); + offset += len; break; - case JPEG_MARKER_DQT:{ + } + case JPEG_MARKER_DQT: + { + GST_LOG ("DQT found"); if ((jpeg_header.q >= 128) && (quant_table_index < Q_TABLE_MAX)) { if (!quant_table_index) { quant_header.length = 0; @@ -378,20 +517,27 @@ gst_rtp_jpeg_pay_handle_buffer (GstBaseRTPPayload * basepayload, quant_table_index++; } offset += gst_rtp_jpeg_pay_header_size (data, offset); - } break; + } case JPEG_MARKER_SOS: sos_found = TRUE; + GST_LOG_OBJECT (pay, "SOS found"); jpeg_header_size = offset + gst_rtp_jpeg_pay_header_size (data, offset); break; case JPEG_MARKER_EOI: - GST_WARNING ("EOI reached before SOS!"); + GST_WARNING_OBJECT (pay, "EOI reached before SOS!"); break; case JPEG_MARKER_SOI: + GST_LOG_OBJECT (pay, "SOI found"); + break; default: break; } } + if (jpeg_header.width == 0 || jpeg_header.height == 0) + goto no_dimension; + + GST_LOG_OBJECT (pay, "header size %u", jpeg_header_size); size -= jpeg_header_size; data += jpeg_header_size; @@ -408,6 +554,7 @@ gst_rtp_jpeg_pay_handle_buffer (GstBaseRTPPayload * basepayload, GST_BUFFER_TIMESTAMP (outbuf) = timestamp; if (payload_size == bytes_left) { + GST_LOG_OBJECT (pay, "last packet of frame"); frame_done = TRUE; gst_rtp_buffer_set_marker (outbuf, 1); } @@ -429,6 +576,8 @@ gst_rtp_jpeg_pay_handle_buffer (GstBaseRTPPayload * basepayload, payload += sizeof (quant_header); for (index = 0; index < quant_table_index; index++) { + GST_LOG_OBJECT (pay, "sending quant data %d, size %d", index, + table_size); memcpy (payload, jpeg_quantizer_table[index], table_size); payload += table_size; } @@ -437,6 +586,7 @@ gst_rtp_jpeg_pay_handle_buffer (GstBaseRTPPayload * basepayload, bytes_left -= quant_data_size; quant_data_size = 0; } + GST_LOG_OBJECT (pay, "sending payload size %d", payload_size); memcpy (payload, &data[offset], payload_size); @@ -458,6 +608,14 @@ gst_rtp_jpeg_pay_handle_buffer (GstBaseRTPPayload * basepayload, gst_buffer_unref (buffer); return ret; + + /* ERRORS */ +no_dimension: + { + GST_ELEMENT_ERROR (pay, STREAM, FORMAT, ("No size given"), (NULL)); + return GST_FLOW_NOT_NEGOTIATED; + } + } static void |