From a9e496d92fe0ce64e91f974a78a87d4ef4357b3c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 8 May 2009 11:35:02 +0200 Subject: [PATCH] rtj2kdepay: add basic JPEG 2000 depayloader --- gst/rtp/Makefile.am | 2 + gst/rtp/gstrtp.c | 4 + gst/rtp/gstrtpj2kdepay.c | 289 +++++++++++++++++++++++++++++++++++++++ gst/rtp/gstrtpj2kdepay.h | 62 +++++++++ 4 files changed, 357 insertions(+) create mode 100644 gst/rtp/gstrtpj2kdepay.c create mode 100644 gst/rtp/gstrtpj2kdepay.h diff --git a/gst/rtp/Makefile.am b/gst/rtp/Makefile.am index cc0b473d67..c578fd402d 100644 --- a/gst/rtp/Makefile.am +++ b/gst/rtp/Makefile.am @@ -32,6 +32,7 @@ libgstrtp_la_SOURCES = \ gstrtph263pay.c \ gstrtph264depay.c \ gstrtph264pay.c \ + gstrtpj2kdepay.c \ gstrtpj2kpay.c \ gstrtpjpegdepay.c \ gstrtpjpegpay.c \ @@ -106,6 +107,7 @@ noinst_HEADERS = \ gstrtph263pay.h \ gstrtph264depay.h \ gstrtph264pay.h \ + gstrtpj2kdepay.h \ gstrtpj2kpay.h \ gstrtpjpegdepay.h \ gstrtpjpegpay.h \ diff --git a/gst/rtp/gstrtp.c b/gst/rtp/gstrtp.c index 2db0f7ef31..720625405f 100644 --- a/gst/rtp/gstrtp.c +++ b/gst/rtp/gstrtp.c @@ -49,6 +49,7 @@ #include "gstrtph263pay.h" #include "gstrtph264depay.h" #include "gstrtph264pay.h" +#include "gstrtpj2kdepay.h" #include "gstrtpj2kpay.h" #include "gstrtpjpegdepay.h" #include "gstrtpjpegpay.h" @@ -163,6 +164,9 @@ plugin_init (GstPlugin * plugin) if (!gst_rtp_h264_pay_plugin_init (plugin)) return FALSE; + if (!gst_rtp_j2k_depay_plugin_init (plugin)) + return FALSE; + if (!gst_rtp_j2k_pay_plugin_init (plugin)) return FALSE; diff --git a/gst/rtp/gstrtpj2kdepay.c b/gst/rtp/gstrtpj2kdepay.c new file mode 100644 index 0000000000..0d467e4194 --- /dev/null +++ b/gst/rtp/gstrtpj2kdepay.c @@ -0,0 +1,289 @@ +/* GStreamer + * Copyright (C) <2009> Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include +#include "gstrtpj2kdepay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpj2kdepay_debug); +#define GST_CAT_DEFAULT (rtpj2kdepay_debug) + +/* elementfactory information */ +static const GstElementDetails gst_rtp_j2kdepay_details = +GST_ELEMENT_DETAILS ("RTP JPEG 2000 depayloader", + "Codec/Depayloader/Network", + "Extracts JPEG 2000 video from RTP packets (RFC 5371)", + "Wim Taymans "); + +static GstStaticPadTemplate gst_rtp_j2k_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("image/x-jpc") + ); + +static GstStaticPadTemplate gst_rtp_j2k_depay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + 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) \"JPEG2000\"") + ); + +GST_BOILERPLATE (GstRtpJ2KDepay, gst_rtp_j2k_depay, GstBaseRTPDepayload, + GST_TYPE_BASE_RTP_DEPAYLOAD); + +static void gst_rtp_j2k_depay_finalize (GObject * object); + +static GstStateChangeReturn +gst_rtp_j2k_depay_change_state (GstElement * element, + GstStateChange transition); + +static gboolean gst_rtp_j2k_depay_setcaps (GstBaseRTPDepayload * depayload, + GstCaps * caps); +static GstBuffer *gst_rtp_j2k_depay_process (GstBaseRTPDepayload * depayload, + GstBuffer * buf); + +static void +gst_rtp_j2k_depay_base_init (gpointer klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_rtp_j2k_depay_src_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_rtp_j2k_depay_sink_template)); + + gst_element_class_set_details (element_class, &gst_rtp_j2kdepay_details); +} + +static void +gst_rtp_j2k_depay_class_init (GstRtpJ2KDepayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseRTPDepayloadClass *gstbasertpdepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstbasertpdepayload_class = (GstBaseRTPDepayloadClass *) klass; + + gobject_class->finalize = gst_rtp_j2k_depay_finalize; + + gstelement_class->change_state = gst_rtp_j2k_depay_change_state; + + gstbasertpdepayload_class->set_caps = gst_rtp_j2k_depay_setcaps; + gstbasertpdepayload_class->process = gst_rtp_j2k_depay_process; + + GST_DEBUG_CATEGORY_INIT (rtpj2kdepay_debug, "rtpj2kdepay", 0, + "J2K Video RTP Depayloader"); +} + +static void +gst_rtp_j2k_depay_init (GstRtpJ2KDepay * rtpj2kdepay, + GstRtpJ2KDepayClass * klass) +{ + rtpj2kdepay->adapter = gst_adapter_new (); +} + +static void +gst_rtp_j2k_depay_finalize (GObject * object) +{ + GstRtpJ2KDepay *rtpj2kdepay; + + rtpj2kdepay = GST_RTP_J2K_DEPAY (object); + + g_object_unref (rtpj2kdepay->adapter); + rtpj2kdepay->adapter = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_rtp_j2k_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps) +{ + GstRtpJ2KDepay *rtpj2kdepay; + GstStructure *structure; + gint clock_rate; + GstCaps *outcaps; + gboolean res; + + rtpj2kdepay = GST_RTP_J2K_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; + + outcaps = + gst_caps_new_simple ("image/x-jpc", "framerate", GST_TYPE_FRACTION, 0, 1, + "fields", G_TYPE_INT, 1, "fourcc", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('s', + 'Y', 'U', 'V'), NULL); + res = gst_pad_set_caps (depayload->srcpad, outcaps); + gst_caps_unref (outcaps); + + return res; +} + +static GstBuffer * +gst_rtp_j2k_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) +{ + GstRtpJ2KDepay *rtpj2kdepay; + GstBuffer *outbuf; + gint payload_len; + guint8 *payload; + guint frag_offset; + + rtpj2kdepay = GST_RTP_J2K_DEPAY (depayload); + + /* flush everything on discont for now */ + if (GST_BUFFER_IS_DISCONT (buf)) { + GST_DEBUG_OBJECT (rtpj2kdepay, "DISCONT, flushing data"); + gst_adapter_clear (rtpj2kdepay->adapter); + rtpj2kdepay->need_header = TRUE; + } + + payload_len = gst_rtp_buffer_get_payload_len (buf); + + if (payload_len < 8) + goto empty_packet; + + payload = gst_rtp_buffer_get_payload (buf); + + /* + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |tp |MHF|mh_id|T| priority | tile number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |reserved | fragment offset | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + frag_offset = (payload[5] << 16) | (payload[6] << 8) | payload[7]; + + GST_DEBUG_OBJECT (rtpj2kdepay, "frag %u", frag_offset); + + if (rtpj2kdepay->need_header) { + if (frag_offset != 0) + goto waiting_header; + + rtpj2kdepay->need_header = FALSE; + } + + payload += 8; + payload_len -= 8; + + /* take JPEG 2000 data, push in the adapter */ + outbuf = gst_rtp_buffer_get_payload_subbuffer (buf, 8, -1); + gst_adapter_push (rtpj2kdepay->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 (rtpj2kdepay->adapter); + GST_DEBUG_OBJECT (rtpj2kdepay, "marker set, last buffer"); + + /* take the last bytes of the JPEG 2000 data to see if there is an EOC + * marker */ + gst_adapter_copy (rtpj2kdepay->adapter, end, avail - 2, 2); + + if (end[0] != 0xff && end[1] != 0xd9) { + GST_DEBUG_OBJECT (rtpj2kdepay, "no EOC 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 (rtpj2kdepay->adapter, outbuf); + avail += 2; + } + outbuf = gst_adapter_take_buffer (rtpj2kdepay->adapter, avail); + + GST_DEBUG_OBJECT (rtpj2kdepay, "returning %u bytes", avail); + } + + return outbuf; + + /* ERRORS */ +empty_packet: + { + GST_ELEMENT_WARNING (rtpj2kdepay, STREAM, DECODE, + ("Empty Payload."), (NULL)); + return NULL; + } +waiting_header: + { + GST_DEBUG_OBJECT (rtpj2kdepay, "we are waiting for a header"); + return NULL; + } +} + +static GstStateChangeReturn +gst_rtp_j2k_depay_change_state (GstElement * element, GstStateChange transition) +{ + GstRtpJ2KDepay *rtpj2kdepay; + GstStateChangeReturn ret; + + rtpj2kdepay = GST_RTP_J2K_DEPAY (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_adapter_clear (rtpj2kdepay->adapter); + rtpj2kdepay->need_header = TRUE; + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_adapter_clear (rtpj2kdepay->adapter); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + return ret; +} + +gboolean +gst_rtp_j2k_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpj2kdepay", + GST_RANK_MARGINAL, GST_TYPE_RTP_J2K_DEPAY); +} diff --git a/gst/rtp/gstrtpj2kdepay.h b/gst/rtp/gstrtpj2kdepay.h new file mode 100644 index 0000000000..c1c8a17967 --- /dev/null +++ b/gst/rtp/gstrtpj2kdepay.h @@ -0,0 +1,62 @@ +/* GStreamer + * Copyright (C) <2009> Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * 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. + */ + +#ifndef __GST_RTP_J2K_DEPAY_H__ +#define __GST_RTP_J2K_DEPAY_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_J2K_DEPAY \ + (gst_rtp_j2k_depay_get_type()) +#define GST_RTP_J2K_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_J2K_DEPAY,GstRtpJ2KDepay)) +#define GST_RTP_J2K_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_J2K_DEPAY,GstRtpJ2KDepayClass)) +#define GST_IS_RTP_J2K_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_J2K_DEPAY)) +#define GST_IS_RTP_J2K_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_J2K_DEPAY)) + +typedef struct _GstRtpJ2KDepay GstRtpJ2KDepay; +typedef struct _GstRtpJ2KDepayClass GstRtpJ2KDepayClass; + +struct _GstRtpJ2KDepay +{ + GstBaseRTPDepayload depayload; + + GstAdapter *adapter; + gboolean need_header; + + gint width, height; +}; + +struct _GstRtpJ2KDepayClass +{ + GstBaseRTPDepayloadClass parent_class; +}; + +gboolean gst_rtp_j2k_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_J2K_DEPAY_H__ */