static GstStateChangeReturn gst_rtp_jpeg_depay_change_state (GstElement * element, GstStateChange transition) { GstRtpJPEGDepay *rtpjpegdepay; GstStateChangeReturn ret; rtpjpegdepay = GST_RTP_JPEG_DEPAY (element); switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: gst_rtp_jpeg_depay_reset (rtpjpegdepay); break; default: break; } ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: break; default: break; } return ret; }
static void gst_rtp_jpeg_depay_finalize (GObject * object) { GstRtpJPEGDepay *rtpjpegdepay; rtpjpegdepay = GST_RTP_JPEG_DEPAY (object); g_object_unref (rtpjpegdepay->adapter); rtpjpegdepay->adapter = NULL; G_OBJECT_CLASS (parent_class)->finalize (object); }
static gboolean gst_rtp_jpeg_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps) { GstRtpJPEGDepay *rtpjpegdepay; GstStructure *structure; gint clock_rate; rtpjpegdepay = GST_RTP_JPEG_DEPAY (depayload); structure = gst_caps_get_structure (caps, 0); if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) clock_rate = 90000; depayload->clock_rate = clock_rate; rtpjpegdepay->width = 0; rtpjpegdepay->height = 0; return TRUE; }
static GstBuffer * gst_rtp_jpeg_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp) { GstRtpJPEGDepay *rtpjpegdepay; GstBuffer *outbuf; gint payload_len, header_len; guint8 *payload; guint frag_offset; gint Q; guint type, width, height; guint16 dri, precision, length; guint8 *qtable; rtpjpegdepay = GST_RTP_JPEG_DEPAY (depayload); if (GST_BUFFER_IS_DISCONT (rtp->buffer)) { GST_DEBUG_OBJECT (depayload, "DISCONT, reset adapter"); gst_adapter_clear (rtpjpegdepay->adapter); rtpjpegdepay->discont = TRUE; } payload_len = gst_rtp_buffer_get_payload_len (rtp); if (payload_len < 8) goto empty_packet; payload = gst_rtp_buffer_get_payload (rtp); header_len = 0; /* 0 1 2 3 * 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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Type-specific | Fragment Offset | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Type | Q | Width | Height | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ frag_offset = (payload[1] << 16) | (payload[2] << 8) | payload[3]; type = payload[4]; Q = payload[5]; width = payload[6] * 8; height = payload[7] * 8; /* saw a packet with fragment offset > 0 and we don't already have data queued * up (most importantly, we don't have a header for this data) -- drop it * XXX: maybe we can check if the jpeg is progressive and salvage the data? * XXX: not implemented yet because jpegenc can't create progressive jpegs */ if (frag_offset > 0 && gst_adapter_available (rtpjpegdepay->adapter) == 0) goto no_header_packet; /* allow frame dimensions > 2040, passed in SDP session or media attributes * from gstrtspsrc.c (gst_rtspsrc_sdp_attributes_to_caps), or in caps */ if (!width) width = rtpjpegdepay->media_width; if (!height) height = rtpjpegdepay->media_height; if (width == 0 || height == 0) goto invalid_dimension; GST_DEBUG_OBJECT (rtpjpegdepay, "frag %u, type %u, Q %d, width %u, height %u", frag_offset, type, Q, width, height); header_len += 8; payload += 8; payload_len -= 8; dri = 0; if (type > 63) { if (payload_len < 4) goto empty_packet; /* 0 1 2 3 * 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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Restart Interval |F|L| Restart Count | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ dri = (payload[0] << 8) | payload[1]; GST_DEBUG_OBJECT (rtpjpegdepay, "DRI %" G_GUINT16_FORMAT, dri); payload += 4; header_len += 4; payload_len -= 4; } if (Q >= 128 && frag_offset == 0) { if (payload_len < 4) goto empty_packet; /* 0 1 2 3 * 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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | MBZ | Precision | Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Quantization Table Data | * | ... | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ precision = payload[1]; length = (payload[2] << 8) | payload[3]; GST_DEBUG_OBJECT (rtpjpegdepay, "precision %04x, length %" G_GUINT16_FORMAT, precision, length); if (Q == 255 && length == 0) goto empty_packet; payload += 4; header_len += 4; payload_len -= 4; if (length > payload_len) goto empty_packet; if (length > 0) qtable = payload; else qtable = rtpjpegdepay->qtables[Q]; payload += length; header_len += length; payload_len -= length; } else { length = 0; qtable = NULL; precision = 0; } if (frag_offset == 0) { GstMapInfo map; guint size; if (rtpjpegdepay->width != width || rtpjpegdepay->height != height) { GstCaps *outcaps; outcaps = gst_caps_new_simple ("image/jpeg", "framerate", GST_TYPE_FRACTION, rtpjpegdepay->frate_num, rtpjpegdepay->frate_denom, "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL); gst_pad_set_caps (depayload->srcpad, outcaps); gst_caps_unref (outcaps); rtpjpegdepay->width = width; rtpjpegdepay->height = height; } GST_LOG_OBJECT (rtpjpegdepay, "first packet, length %" G_GUINT16_FORMAT, length); /* first packet */ if (length == 0) { if (Q < 128) { /* no quant table, see if we have one cached */ qtable = rtpjpegdepay->qtables[Q]; if (!qtable) { GST_DEBUG_OBJECT (rtpjpegdepay, "making Q %d table", Q); /* make and cache the table */ qtable = g_new (guint8, 128); MakeTables (rtpjpegdepay, Q, qtable); rtpjpegdepay->qtables[Q] = qtable; } else { GST_DEBUG_OBJECT (rtpjpegdepay, "using cached table for Q %d", Q); } /* all 8 bit quantizers */ precision = 0; } else { if (!qtable) goto no_qtable; } } /* I think we can get here with a NULL qtable, so make sure we don't go dereferencing it in MakeHeaders if we do */ if (!qtable) goto no_qtable; /* max header length, should be big enough */ outbuf = gst_buffer_new_and_alloc (1000); gst_buffer_map (outbuf, &map, GST_MAP_WRITE); size = MakeHeaders (map.data, type, width, height, qtable, precision, dri); gst_buffer_unmap (outbuf, &map); gst_buffer_resize (outbuf, 0, size); GST_DEBUG_OBJECT (rtpjpegdepay, "pushing %u bytes of header", size); gst_adapter_push (rtpjpegdepay->adapter, outbuf); } /* take JPEG data, push in the adapter */ GST_DEBUG_OBJECT (rtpjpegdepay, "pushing data at offset %d", header_len); outbuf = gst_rtp_buffer_get_payload_subbuffer (rtp, header_len, -1); gst_adapter_push (rtpjpegdepay->adapter, outbuf); outbuf = NULL; if (gst_rtp_buffer_get_marker (rtp)) { guint avail; guint8 end[2]; GstMapInfo map; /* last buffer take all data out of the adapter */ avail = gst_adapter_available (rtpjpegdepay->adapter); GST_DEBUG_OBJECT (rtpjpegdepay, "marker set, last buffer"); if (avail < 2) goto invalid_packet; /* take the last bytes of the jpeg data to see if there is an EOI * marker */ gst_adapter_copy (rtpjpegdepay->adapter, end, avail - 2, 2); if (end[0] != 0xff && end[1] != 0xd9) { GST_DEBUG_OBJECT (rtpjpegdepay, "no EOI marker, adding one"); /* no EOI marker, add one */ outbuf = gst_buffer_new_and_alloc (2); gst_buffer_map (outbuf, &map, GST_MAP_WRITE); map.data[0] = 0xff; map.data[1] = 0xd9; gst_buffer_unmap (outbuf, &map); gst_adapter_push (rtpjpegdepay->adapter, outbuf); avail += 2; } outbuf = gst_adapter_take_buffer (rtpjpegdepay->adapter, avail); if (rtpjpegdepay->discont) { GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); rtpjpegdepay->discont = FALSE; } gst_rtp_drop_meta (GST_ELEMENT_CAST (rtpjpegdepay), outbuf, g_quark_from_static_string (GST_META_TAG_VIDEO_STR)); GST_DEBUG_OBJECT (rtpjpegdepay, "returning %u bytes", avail); } return outbuf; /* ERRORS */ empty_packet: { GST_ELEMENT_WARNING (rtpjpegdepay, STREAM, DECODE, ("Empty Payload."), (NULL)); return NULL; } invalid_dimension: { GST_ELEMENT_WARNING (rtpjpegdepay, STREAM, FORMAT, ("Invalid Dimension %dx%d.", width, height), (NULL)); return NULL; } no_qtable: { GST_WARNING_OBJECT (rtpjpegdepay, "no qtable"); return NULL; } invalid_packet: { GST_WARNING_OBJECT (rtpjpegdepay, "invalid packet"); gst_adapter_flush (rtpjpegdepay->adapter, gst_adapter_available (rtpjpegdepay->adapter)); return NULL; } no_header_packet: { GST_WARNING_OBJECT (rtpjpegdepay, "discarding data packets received when we have no header"); return NULL; } }
static gboolean gst_rtp_jpeg_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) { GstRtpJPEGDepay *rtpjpegdepay; GstStructure *structure; gint clock_rate; const gchar *media_attr; rtpjpegdepay = GST_RTP_JPEG_DEPAY (depayload); structure = gst_caps_get_structure (caps, 0); GST_DEBUG_OBJECT (rtpjpegdepay, "Caps set: %" GST_PTR_FORMAT, caps); if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) clock_rate = 90000; depayload->clock_rate = clock_rate; /* reset defaults */ rtpjpegdepay->width = 0; rtpjpegdepay->height = 0; rtpjpegdepay->media_width = 0; rtpjpegdepay->media_height = 0; rtpjpegdepay->frate_num = 0; rtpjpegdepay->frate_denom = 1; /* check for optional SDP attributes */ if ((media_attr = gst_structure_get_string (structure, "x-dimensions"))) { gint w, h; if (sscanf (media_attr, "%d,%d", &w, &h) == 2) { rtpjpegdepay->media_width = w; rtpjpegdepay->media_height = h; } } /* try to get a framerate */ media_attr = gst_structure_get_string (structure, "a-framerate"); if (!media_attr) media_attr = gst_structure_get_string (structure, "x-framerate"); if (media_attr) { GValue src = { 0 }; GValue dest = { 0 }; gchar *s; /* canonicalise floating point string so we can handle framerate strings * in the form "24.930" or "24,930" irrespective of the current locale */ s = g_strdup (media_attr); g_strdelimit (s, ",", '.'); /* convert the float to a fraction */ g_value_init (&src, G_TYPE_DOUBLE); g_value_set_double (&src, g_ascii_strtod (s, NULL)); g_value_init (&dest, GST_TYPE_FRACTION); g_value_transform (&src, &dest); rtpjpegdepay->frate_num = gst_value_get_fraction_numerator (&dest); rtpjpegdepay->frate_denom = gst_value_get_fraction_denominator (&dest); g_free (s); } return TRUE; }
static GstBuffer * gst_rtp_jpeg_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) { GstRtpJPEGDepay *rtpjpegdepay; GstBuffer *outbuf; gint payload_len, header_len; guint8 *payload; guint frag_offset; gint Q; guint type, width, height; guint16 dri, precision, length; guint8 *qtable; rtpjpegdepay = GST_RTP_JPEG_DEPAY (depayload); if (GST_BUFFER_IS_DISCONT (buf)) { gst_adapter_clear (rtpjpegdepay->adapter); } payload_len = gst_rtp_buffer_get_payload_len (buf); if (payload_len < 8) goto empty_packet; payload = gst_rtp_buffer_get_payload (buf); header_len = 0; /* 0 1 2 3 * 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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Type-specific | Fragment Offset | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Type | Q | Width | Height | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ frag_offset = (payload[1] << 16) | (payload[2] << 8) | payload[3]; type = payload[4]; Q = payload[5]; width = payload[6] * 8; height = payload[7] * 8; if (width == 0 || height == 0) goto invalid_dimension; GST_DEBUG_OBJECT (rtpjpegdepay, "frag %u, type %u, Q %d, width %u, height %u", frag_offset, type, Q, width, height); header_len += 8; payload += 8; payload_len -= 8; dri = 0; if (type > 63) { if (payload_len < 4) goto empty_packet; /* 0 1 2 3 * 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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Restart Interval |F|L| Restart Count | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ dri = (payload[0] << 8) | payload[1]; GST_DEBUG_OBJECT (rtpjpegdepay, "DRI %" G_GUINT16_FORMAT, dri); payload += 4; header_len += 4; payload_len -= 4; } if (Q >= 128 && frag_offset == 0) { if (payload_len < 4) goto empty_packet; /* 0 1 2 3 * 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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | MBZ | Precision | Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Quantization Table Data | * | ... | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ precision = payload[1]; length = (payload[2] << 8) | payload[3]; GST_DEBUG_OBJECT (rtpjpegdepay, "precision %04x, length %" G_GUINT16_FORMAT, precision, length); if (Q == 255 && length == 0) goto empty_packet; payload += 4; header_len += 4; payload_len -= 4; if (length > payload_len) goto empty_packet; if (length > 0) qtable = payload; else qtable = rtpjpegdepay->qtables[Q]; payload += length; header_len += length; payload_len -= length; } else { length = 0; qtable = NULL; precision = 0; } if (frag_offset == 0) { guint size; if (rtpjpegdepay->width != width || rtpjpegdepay->height != height) { GstCaps *outcaps; outcaps = gst_caps_new_simple ("image/jpeg", "framerate", GST_TYPE_FRACTION, 0, 1, "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL); gst_pad_set_caps (depayload->srcpad, outcaps); gst_caps_unref (outcaps); rtpjpegdepay->width = width; rtpjpegdepay->height = height; } GST_LOG_OBJECT (rtpjpegdepay, "first packet, length %" G_GUINT16_FORMAT, length); /* first packet */ if (length == 0) { if (Q < 128) { /* no quant table, see if we have one cached */ qtable = rtpjpegdepay->qtables[Q]; if (!qtable) { GST_DEBUG_OBJECT (rtpjpegdepay, "making Q %d table", Q); /* make and cache the table */ qtable = g_new (guint8, 128); MakeTables (rtpjpegdepay, Q, qtable); rtpjpegdepay->qtables[Q] = qtable; } else { GST_DEBUG_OBJECT (rtpjpegdepay, "using cached table for Q %d", Q); } /* all 8 bit quantizers */ precision = 0; } else { if (!qtable) goto no_qtable; } } /* max header length, should be big enough */ outbuf = gst_buffer_new_and_alloc (1000); size = MakeHeaders (GST_BUFFER_DATA (outbuf), type, width, height, qtable, precision, dri); GST_DEBUG_OBJECT (rtpjpegdepay, "pushing %u bytes of header", size); GST_BUFFER_SIZE (outbuf) = size; gst_adapter_push (rtpjpegdepay->adapter, outbuf); } /* take JPEG data, push in the adapter */ GST_DEBUG_OBJECT (rtpjpegdepay, "pushing data at offset %d", header_len); outbuf = gst_rtp_buffer_get_payload_subbuffer (buf, header_len, -1); gst_adapter_push (rtpjpegdepay->adapter, outbuf); outbuf = NULL; if (gst_rtp_buffer_get_marker (buf)) { guint avail; guint8 end[2]; guint8 *data; /* last buffer take all data out of the adapter */ avail = gst_adapter_available (rtpjpegdepay->adapter); GST_DEBUG_OBJECT (rtpjpegdepay, "marker set, last buffer"); /* take the last bytes of the jpeg data to see if there is an EOI * marker */ gst_adapter_copy (rtpjpegdepay->adapter, end, avail - 2, 2); if (end[0] != 0xff && end[1] != 0xd9) { GST_DEBUG_OBJECT (rtpjpegdepay, "no EOI marker, adding one"); /* no EOI marker, add one */ outbuf = gst_buffer_new_and_alloc (2); data = GST_BUFFER_DATA (outbuf); data[0] = 0xff; data[1] = 0xd9; gst_adapter_push (rtpjpegdepay->adapter, outbuf); avail += 2; } outbuf = gst_adapter_take_buffer (rtpjpegdepay->adapter, avail); GST_DEBUG_OBJECT (rtpjpegdepay, "returning %u bytes", avail); } return outbuf; /* ERRORS */ empty_packet: { GST_ELEMENT_WARNING (rtpjpegdepay, STREAM, DECODE, ("Empty Payload."), (NULL)); return NULL; } invalid_dimension: { GST_ELEMENT_WARNING (rtpjpegdepay, STREAM, FORMAT, ("Invalid Dimension %dx%d.", width, height), (NULL)); return NULL; } no_qtable: { GST_WARNING_OBJECT (rtpjpegdepay, "no qtable"); return NULL; } }