*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#endif
#include <gst/rtp/gstrtpbuffer.h>
+#include <gst/video/video.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "gstrtpjpegdepay.h"
+#include "gstrtputils.h"
GST_DEBUG_CATEGORY_STATIC (rtpjpegdepay_debug);
#define GST_CAT_DEFAULT (rtpjpegdepay_debug)
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("application/x-rtp, "
"media = (string) \"video\", "
- "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
"clock-rate = (int) 90000, " "encoding-name = (string) \"JPEG\"; "
/* optional SDP attributes */
/*
#define gst_rtp_jpeg_depay_parent_class parent_class
G_DEFINE_TYPE (GstRtpJPEGDepay, gst_rtp_jpeg_depay,
- GST_TYPE_BASE_RTP_DEPAYLOAD);
+ GST_TYPE_RTP_BASE_DEPAYLOAD);
static void gst_rtp_jpeg_depay_finalize (GObject * object);
static GstStateChangeReturn gst_rtp_jpeg_depay_change_state (GstElement *
element, GstStateChange transition);
-static gboolean gst_rtp_jpeg_depay_setcaps (GstBaseRTPDepayload * depayload,
+static gboolean gst_rtp_jpeg_depay_setcaps (GstRTPBaseDepayload * depayload,
GstCaps * caps);
-static GstBuffer *gst_rtp_jpeg_depay_process (GstBaseRTPDepayload * depayload,
- GstBuffer * buf);
+static GstBuffer *gst_rtp_jpeg_depay_process (GstRTPBaseDepayload * depayload,
+ GstRTPBuffer * rtp);
static void
gst_rtp_jpeg_depay_class_init (GstRtpJPEGDepayClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
- GstBaseRTPDepayloadClass *gstbasertpdepayload_class;
+ GstRTPBaseDepayloadClass *gstrtpbasedepayload_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
- gstbasertpdepayload_class = (GstBaseRTPDepayloadClass *) klass;
+ gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass;
gobject_class->finalize = gst_rtp_jpeg_depay_finalize;
- gst_element_class_add_pad_template (gstelement_class,
- gst_static_pad_template_get (&gst_rtp_jpeg_depay_src_template));
- gst_element_class_add_pad_template (gstelement_class,
- gst_static_pad_template_get (&gst_rtp_jpeg_depay_sink_template));
+ gst_element_class_add_static_pad_template (gstelement_class,
+ &gst_rtp_jpeg_depay_src_template);
+ gst_element_class_add_static_pad_template (gstelement_class,
+ &gst_rtp_jpeg_depay_sink_template);
- gst_element_class_set_details_simple (gstelement_class,
+ gst_element_class_set_static_metadata (gstelement_class,
"RTP JPEG depayloader", "Codec/Depayloader/Network/RTP",
"Extracts JPEG video from RTP packets (RFC 2435)",
"Wim Taymans <wim.taymans@gmail.com>");
gstelement_class->change_state = gst_rtp_jpeg_depay_change_state;
- gstbasertpdepayload_class->set_caps = gst_rtp_jpeg_depay_setcaps;
- gstbasertpdepayload_class->process = gst_rtp_jpeg_depay_process;
+ gstrtpbasedepayload_class->set_caps = gst_rtp_jpeg_depay_setcaps;
+ gstrtpbasedepayload_class->process_rtp_packet = gst_rtp_jpeg_depay_process;
GST_DEBUG_CATEGORY_INIT (rtpjpegdepay_debug, "rtpjpegdepay", 0,
"JPEG Video RTP Depayloader");
G_OBJECT_CLASS (parent_class)->finalize (object);
}
+static const int zigzag[] = {
+ 0, 1, 8, 16, 9, 2, 3, 10,
+ 17, 24, 32, 25, 18, 11, 4, 5,
+ 12, 19, 26, 33, 40, 48, 41, 34,
+ 27, 20, 13, 6, 7, 14, 21, 28,
+ 35, 42, 49, 56, 57, 50, 43, 36,
+ 29, 22, 15, 23, 30, 37, 44, 51,
+ 58, 59, 52, 45, 38, 31, 39, 46,
+ 53, 60, 61, 54, 47, 55, 62, 63
+};
+
/*
* Table K.1 from JPEG spec.
*/
Q = 200 - factor * 2;
for (i = 0; i < 64; i++) {
- gint lq = (jpeg_luma_quantizer[i] * Q + 50) / 100;
- gint cq = (jpeg_chroma_quantizer[i] * Q + 50) / 100;
+ gint lq = (jpeg_luma_quantizer[zigzag[i]] * Q + 50) / 100;
+ gint cq = (jpeg_chroma_quantizer[zigzag[i]] * Q + 50) / 100;
/* Limit the quantizers to 1 <= q <= 255 */
qtable[i] = CLAMP (lq, 1, 255);
*p++ = 0x11; /* huffman table 1 */
*p++ = 0; /* first DCT coeff */
*p++ = 63; /* last DCT coeff */
- *p++ = 0; /* sucessive approx. */
+ *p++ = 0; /* successive approx. */
return (p - start);
};
static gboolean
-gst_rtp_jpeg_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps)
+gst_rtp_jpeg_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
{
GstRtpJPEGDepay *rtpjpegdepay;
GstStructure *structure;
}
static GstBuffer *
-gst_rtp_jpeg_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
+gst_rtp_jpeg_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
{
GstRtpJPEGDepay *rtpjpegdepay;
GstBuffer *outbuf;
guint type, width, height;
guint16 dri, precision, length;
guint8 *qtable;
- GstRTPBuffer rtp;
rtpjpegdepay = GST_RTP_JPEG_DEPAY (depayload);
- if (GST_BUFFER_IS_DISCONT (buf)) {
+ if (GST_BUFFER_IS_DISCONT (rtp->buffer)) {
+ GST_DEBUG_OBJECT (depayload, "DISCONT, reset adapter");
gst_adapter_clear (rtpjpegdepay->adapter);
rtpjpegdepay->discont = TRUE;
}
- gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp);
- payload_len = gst_rtp_buffer_get_payload_len (&rtp);
+ payload_len = gst_rtp_buffer_get_payload_len (rtp);
if (payload_len < 8)
goto empty_packet;
- payload = gst_rtp_buffer_get_payload (&rtp);
+ payload = gst_rtp_buffer_get_payload (rtp);
header_len = 0;
/* 0 1 2 3
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)
}
if (frag_offset == 0) {
+ GstMapInfo map;
guint size;
- guint8 *data;
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_caps_new_simple ("image/jpeg", "parsed", G_TYPE_BOOLEAN, TRUE,
+ "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);
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);
- data = gst_buffer_map (outbuf, NULL, NULL, GST_MAP_WRITE);
- size = MakeHeaders (data, type, width, height, qtable, precision, dri);
- gst_buffer_unmap (outbuf, data, size);
+ 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 %" G_GSIZE_FORMAT " bytes of header", 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);
+ 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)) {
+ if (gst_rtp_buffer_get_marker (rtp)) {
guint avail;
guint8 end[2];
- guint8 *data;
+ 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);
/* no EOI marker, add one */
outbuf = gst_buffer_new_and_alloc (2);
- data = gst_buffer_map (outbuf, NULL, NULL, GST_MAP_WRITE);
- data[0] = 0xff;
- data[1] = 0xd9;
- gst_buffer_unmap (outbuf, data, -1);
+ 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;
rtpjpegdepay->discont = FALSE;
}
+ gst_rtp_drop_non_video_meta (rtpjpegdepay, outbuf);
+
GST_DEBUG_OBJECT (rtpjpegdepay, "returning %u bytes", avail);
}
- gst_rtp_buffer_unmap (&rtp);
-
return outbuf;
/* ERRORS */
{
GST_ELEMENT_WARNING (rtpjpegdepay, STREAM, DECODE,
("Empty Payload."), (NULL));
- gst_rtp_buffer_unmap (&rtp);
return NULL;
}
invalid_dimension:
{
GST_ELEMENT_WARNING (rtpjpegdepay, STREAM, FORMAT,
("Invalid Dimension %dx%d.", width, height), (NULL));
- gst_rtp_buffer_unmap (&rtp);
return NULL;
}
no_qtable:
{
GST_WARNING_OBJECT (rtpjpegdepay, "no qtable");
- gst_rtp_buffer_unmap (&rtp);
+ 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;
}
}