summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWim Taymans <wim.taymans@collabora.co.uk>2010-12-09 10:44:39 +0100
committerWim Taymans <wim.taymans@collabora.co.uk>2010-12-20 12:50:39 +0100
commit8f0b45ee5e969da53db6490e14703fabfd38d5d1 (patch)
tree3a1a4850e73063bb02daed0ca0de6dc7305cd50f
parent6cb0efede4f3c208c09855172c362b40252a96f2 (diff)
rtp: add wj2 pay and depayloader
-rw-r--r--gst/rtp/Makefile.am8
-rw-r--r--gst/rtp/gstrtp.c8
-rw-r--r--gst/rtp/gstrtpwj2depay.c756
-rw-r--r--gst/rtp/gstrtpwj2depay.h78
-rw-r--r--gst/rtp/gstrtpwj2pay.c526
-rw-r--r--gst/rtp/gstrtpwj2pay.h61
6 files changed, 1435 insertions, 2 deletions
diff --git a/gst/rtp/Makefile.am b/gst/rtp/Makefile.am
index 5a300b33c..6c42c3758 100644
--- a/gst/rtp/Makefile.am
+++ b/gst/rtp/Makefile.am
@@ -69,7 +69,9 @@ libgstrtp_la_SOURCES = \
gstrtpvorbisdepay.c \
gstrtpvorbispay.c \
gstrtpvrawdepay.c \
- gstrtpvrawpay.c
+ gstrtpvrawpay.c \
+ gstrtpwj2depay.c \
+ gstrtpwj2pay.c
if HAVE_WINSOCK2_H
@@ -156,4 +158,6 @@ noinst_HEADERS = \
gstrtpvorbisdepay.h \
gstrtpvorbispay.h \
gstrtpvrawdepay.h \
- gstrtpvrawpay.h
+ gstrtpvrawpay.h \
+ gstrtpwj2depay.h \
+ gstrtpwj2pay.h
diff --git a/gst/rtp/gstrtp.c b/gst/rtp/gstrtp.c
index 339ff9747..db05669b3 100644
--- a/gst/rtp/gstrtp.c
+++ b/gst/rtp/gstrtp.c
@@ -87,6 +87,8 @@
#include "gstrtpvorbispay.h"
#include "gstrtpvrawdepay.h"
#include "gstrtpvrawpay.h"
+#include "gstrtpwj2depay.h"
+#include "gstrtpwj2pay.h"
static gboolean
plugin_init (GstPlugin * plugin)
@@ -289,6 +291,12 @@ plugin_init (GstPlugin * plugin)
if (!gst_rtp_vraw_pay_plugin_init (plugin))
return FALSE;
+ if (!gst_rtp_wj2_depay_plugin_init (plugin))
+ return FALSE;
+
+ if (!gst_rtp_wj2_pay_plugin_init (plugin))
+ return FALSE;
+
return TRUE;
}
diff --git a/gst/rtp/gstrtpwj2depay.c b/gst/rtp/gstrtpwj2depay.c
new file mode 100644
index 000000000..9603684d4
--- /dev/null
+++ b/gst/rtp/gstrtpwj2depay.c
@@ -0,0 +1,756 @@
+/* GStreamer
+ * Copyright (C) <2010> Wim Taymans <wim.taymans@gmail.com>
+ *
+ * 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 <gst/rtp/gstrtpbuffer.h>
+
+#include <string.h>
+#include "gstrtpwj2depay.h"
+
+GST_DEBUG_CATEGORY_STATIC (rtpwj2depay_debug);
+#define GST_CAT_DEFAULT (rtpwj2depay_debug)
+
+static GstStaticPadTemplate gst_rtp_wj2_depay_src_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("image/x-wj2")
+ );
+
+static GstStaticPadTemplate gst_rtp_wj2_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) \"X-WJ2\"")
+ );
+
+GST_BOILERPLATE (GstRtpWJ2Depay, gst_rtp_wj2_depay, GstBaseRTPDepayload,
+ GST_TYPE_BASE_RTP_DEPAYLOAD);
+
+static void gst_rtp_wj2_depay_finalize (GObject * object);
+
+static GstStateChangeReturn
+gst_rtp_wj2_depay_change_state (GstElement * element,
+ GstStateChange transition);
+
+static gboolean gst_rtp_wj2_depay_setcaps (GstBaseRTPDepayload * depayload,
+ GstCaps * caps);
+static GstBuffer *gst_rtp_wj2_depay_process (GstBaseRTPDepayload * depayload,
+ GstBuffer * buf);
+
+static void
+gst_rtp_wj2_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_wj2_depay_src_template));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&gst_rtp_wj2_depay_sink_template));
+
+ gst_element_class_set_details_simple (element_class,
+ "RTP WJ2 depayloader", "Codec/Depayloader/Network",
+ "Extracts WJ2 video from RTP packets",
+ "Wim Taymans <wim.taymans@gmail.com>");
+}
+
+static void
+gst_rtp_wj2_depay_class_init (GstRtpWJ2DepayClass * 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_wj2_depay_finalize;
+
+ gstelement_class->change_state = gst_rtp_wj2_depay_change_state;
+
+ gstbasertpdepayload_class->set_caps = gst_rtp_wj2_depay_setcaps;
+ gstbasertpdepayload_class->process = gst_rtp_wj2_depay_process;
+
+ GST_DEBUG_CATEGORY_INIT (rtpwj2depay_debug, "rtpwj2depay", 0,
+ "WJ2 Video RTP Depayloader");
+}
+
+static void
+gst_rtp_wj2_store_wheader (GstRtpWJ2Depay * rtpwj2depay, guint idx,
+ GstBuffer * buf)
+{
+ GstBuffer *old;
+
+ GST_DEBUG_OBJECT (rtpwj2depay, "storing WJ2 header %p at index %u", buf, idx);
+ if ((old = rtpwj2depay->WH[idx]))
+ gst_buffer_unref (old);
+ rtpwj2depay->WH[idx] = buf;
+ /* after we completed the header, we can accept packets with any WH_id */
+ rtpwj2depay->WH_id = -1;
+}
+
+static void
+gst_rtp_wj2_store_mheader (GstRtpWJ2Depay * rtpwj2depay, guint idx,
+ GstBuffer * buf)
+{
+ GstBuffer *old;
+
+ GST_DEBUG_OBJECT (rtpwj2depay, "storing main header %p at index %u", buf,
+ idx);
+ if ((old = rtpwj2depay->MH[idx]))
+ gst_buffer_unref (old);
+ rtpwj2depay->MH[idx] = buf;
+}
+
+static void
+gst_rtp_wj2_clear_caches (GstRtpWJ2Depay * rtpwj2depay)
+{
+ guint i;
+
+ for (i = 0; i < 16; i++)
+ gst_rtp_wj2_store_wheader (rtpwj2depay, i, NULL);
+
+ for (i = 0; i < 8; i++)
+ gst_rtp_wj2_store_mheader (rtpwj2depay, i, NULL);
+}
+
+static void
+gst_rtp_wj2_clear_adapter (GstRtpWJ2Depay * rtpwj2depay, gboolean reset_offset)
+{
+ GST_LOG_OBJECT (rtpwj2depay, "clearing adapter");
+ gst_adapter_clear (rtpwj2depay->adapter);
+ rtpwj2depay->WH_id = -1;
+ rtpwj2depay->MH_id = -1;
+ if (reset_offset)
+ rtpwj2depay->offset = 0;
+ rtpwj2depay->state = GST_RTP_WJ2_STATE_NONE;
+}
+
+static void
+gst_rtp_wj2_store_tile (GstRtpWJ2Depay * rtpwj2depay, guint idx,
+ GstBuffer * buf)
+{
+ if (idx < rtpwj2depay->tiles->len) {
+ GstBuffer *old;
+
+ GST_DEBUG_OBJECT (rtpwj2depay, "storing tile %p at index %u", buf, idx);
+
+ if ((old = g_ptr_array_index (rtpwj2depay->tiles, idx)))
+ gst_buffer_unref (old);
+ g_ptr_array_index (rtpwj2depay->tiles, idx) = buf;
+ } else {
+ GST_DEBUG_OBJECT (rtpwj2depay, "invalid tile index %u", idx);
+ if (buf)
+ gst_buffer_unref (buf);
+ }
+}
+
+static void
+gst_rtp_wj2_clear_tiles (GstRtpWJ2Depay * rtpwj2depay)
+{
+ guint i, len;
+
+ len = rtpwj2depay->tiles->len;
+ for (i = 0; i < len; i++)
+ gst_rtp_wj2_store_tile (rtpwj2depay, i, NULL);
+}
+
+static void
+free_buffer (gpointer data)
+{
+ if (data)
+ gst_buffer_unref (GST_BUFFER_CAST (data));
+}
+
+static void
+gst_rtp_wj2_depay_init (GstRtpWJ2Depay * rtpwj2depay,
+ GstRtpWJ2DepayClass * klass)
+{
+ rtpwj2depay->adapter = gst_adapter_new ();
+ rtpwj2depay->tiles = g_ptr_array_new_with_free_func (free_buffer);
+}
+
+static void
+gst_rtp_wj2_depay_finalize (GObject * object)
+{
+ GstRtpWJ2Depay *rtpwj2depay;
+
+ rtpwj2depay = GST_RTP_WJ2_DEPAY (object);
+
+ gst_rtp_wj2_clear_caches (rtpwj2depay);
+ gst_rtp_wj2_clear_tiles (rtpwj2depay);
+
+ g_object_unref (rtpwj2depay->adapter);
+ g_ptr_array_free (rtpwj2depay->tiles, TRUE);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gboolean
+gst_rtp_wj2_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps)
+{
+ GstStructure *structure;
+ gint clock_rate;
+ GstCaps *outcaps;
+ gboolean res;
+
+ 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-wj2", NULL);
+ res = gst_pad_set_caps (depayload->srcpad, outcaps);
+ gst_caps_unref (outcaps);
+
+ return res;
+}
+
+/* parse and store the WJ2 header packet */
+static GstBuffer *
+gst_rtp_wj2_flush_frame (GstRtpWJ2Depay * rtpwj2depay)
+{
+ GstBuffer *wheader, *buf, *out = NULL;
+ gint WH_id, MH_id, size, wsize;
+ guint Xsiz, Ysiz, XOsiz, YOsiz, XTsiz, YTsiz, XTOsiz, YTOsiz;
+ guint TC_count, T_count, C_count;
+ guint8 *data, *wdata;
+ guint offset, i;
+ GstBufferList *blist;
+ GstBufferListIterator *it;
+
+ WH_id = rtpwj2depay->WH_id;
+ MH_id = rtpwj2depay->MH_id;
+ if (WH_id == -1 || MH_id == -1)
+ goto done;
+
+ if ((wheader = rtpwj2depay->WH[WH_id]) == NULL)
+ goto done;
+
+ size = GST_BUFFER_SIZE (wheader);
+ data = GST_BUFFER_DATA (wheader);
+
+ Xsiz = GST_READ_UINT32_BE (data);
+ Ysiz = GST_READ_UINT32_BE (data + 4);
+ XOsiz = GST_READ_UINT32_BE (data + 8);
+ YOsiz = GST_READ_UINT32_BE (data + 12);
+ XTsiz = GST_READ_UINT32_BE (data + 16);
+ YTsiz = GST_READ_UINT32_BE (data + 20);
+ XTOsiz = GST_READ_UINT32_BE (data + 24);
+ YTOsiz = GST_READ_UINT32_BE (data + 28);
+
+ GST_DEBUG_OBJECT (rtpwj2depay, "Xsiz %u, Ysiz %u, XOsiz %u, YOsiz %u", Xsiz,
+ Ysiz, XOsiz, YOsiz);
+ GST_DEBUG_OBJECT (rtpwj2depay, "XTsiz %u, YTsiz %u, XTOsiz %u, YTOsiz %u",
+ XTsiz, YTsiz, XTOsiz, YTOsiz);
+
+ TC_count = (size - 32) / 4;
+ T_count = (Xsiz / XTsiz) * (Ysiz / YTsiz);
+ C_count = TC_count / T_count;
+
+ GST_DEBUG_OBJECT (rtpwj2depay, "TC %u, T %u, C %u", TC_count, T_count,
+ C_count);
+
+ /* calculate the total size of the header */
+ wsize = 42 + 32 + (TC_count * 9);
+ buf = gst_buffer_new_and_alloc (wsize);
+ wdata = GST_BUFFER_DATA (buf);
+ offset = wsize;
+
+ GST_DEBUG_OBJECT (rtpwj2depay, "Header size %u", wsize);
+
+ /* fill partial header */
+ GST_WRITE_UINT32_LE (wdata, 0x20324a57);
+ GST_WRITE_UINT32_LE (wdata + 8, Xsiz);
+ GST_WRITE_UINT32_LE (wdata + 12, Ysiz);
+ GST_WRITE_UINT32_LE (wdata + 16, XOsiz);
+ GST_WRITE_UINT32_LE (wdata + 20, YOsiz);
+ GST_WRITE_UINT32_LE (wdata + 24, XTsiz);
+ GST_WRITE_UINT32_LE (wdata + 28, YTsiz);
+ GST_WRITE_UINT32_LE (wdata + 32, XTOsiz);
+ GST_WRITE_UINT32_LE (wdata + 36, YTOsiz);
+ wdata[40] = T_count;
+ wdata[41] = C_count;
+
+ /* calculate data offsets and length */
+ for (i = 0; i < TC_count; i++) {
+ guint length;
+ GstBuffer *J_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
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | MBZ | CT | COD_STYLE | VFORMAT | version |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ wdata[74 + (9 * i)] = data[32 + (4 * i)] & 0xf;
+ /* fill in offset */
+ GST_WRITE_UINT32_LE (wdata + 75 + (9 * i), offset);
+ /* take the data buffer and its length */
+ J_buf = g_ptr_array_index (rtpwj2depay->tiles, i);
+ if (J_buf == NULL)
+ goto missing_buffer;
+
+ length = GST_BUFFER_SIZE (J_buf);
+ /* align length and add ADV header length */
+ length = GST_ROUND_UP_32 (length) + 16;
+ /* write length */
+ GST_WRITE_UINT32_LE (wdata + 79 + (9 * i), length);
+
+ GST_DEBUG_OBJECT (rtpwj2depay, "length %u, size %u", length,
+ GST_BUFFER_SIZE (J_buf));
+
+ offset += length;
+ }
+ /* write the total file length */
+ GST_WRITE_UINT32_LE (wdata + 4, offset);
+
+ blist = gst_buffer_list_new ();
+ it = gst_buffer_list_iterate (blist);
+ gst_buffer_list_iterator_add_group (it);
+ gst_buffer_list_iterator_add (it, buf);
+
+ /* Add ADV headers and data */
+ for (i = 0; i < TC_count; i++) {
+ guint length;
+ GstBuffer *J_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
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | MBZ | CT | COD_STYLE | VFORMAT | version |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ buf = gst_buffer_new_and_alloc (16);
+ wdata = GST_BUFFER_DATA (buf);
+
+ GST_WRITE_UINT32_LE (wdata, 0xfffffff0);
+ GST_WRITE_UINT32_LE (wdata + 4, 0);
+ wdata[8] = data[33 + (4 * i)];
+ wdata[9] = data[34 + (4 * i)];
+ wdata[10] = 0;
+ wdata[11] = data[35 + (4 * i)];
+
+ /* take the data buffer and its length */
+ J_buf = g_ptr_array_index (rtpwj2depay->tiles, i);
+ wsize = GST_BUFFER_SIZE (J_buf);
+ /* align length and add ADV header length */
+ length = GST_ROUND_UP_32 (wsize);
+ GST_WRITE_UINT32_LE (wdata + 12, length);
+
+ /* add ADV header and J2K data */
+ gst_buffer_list_iterator_add (it, buf);
+ gst_buffer_list_iterator_add (it, J_buf);
+ if (length > wsize) {
+ /* add padding */
+ buf = gst_buffer_new_and_alloc (length - wsize);
+ memset (GST_BUFFER_DATA (buf), 0, GST_BUFFER_SIZE (buf));
+ gst_buffer_list_iterator_add (it, buf);
+ }
+ }
+ gst_buffer_list_iterator_free (it);
+
+ it = gst_buffer_list_iterate (blist);
+ gst_buffer_list_iterator_next_group (it);
+ out = gst_buffer_list_iterator_merge_group (it);
+ gst_buffer_list_iterator_free (it);
+
+done:
+ gst_rtp_wj2_clear_tiles (rtpwj2depay);
+ gst_rtp_wj2_clear_adapter (rtpwj2depay, TRUE);
+
+ return out;
+
+ /* ERRORS */
+missing_buffer:
+ {
+ GST_DEBUG_OBJECT (rtpwj2depay, "missing buffer");
+ goto done;
+ }
+}
+
+static void
+gst_rtp_wj2_new_state (GstRtpWJ2Depay * rtpwj2depay, GstRtpWJ2State state)
+{
+ GstRtpWJ2State old = rtpwj2depay->state;
+
+ GST_DEBUG_OBJECT (rtpwj2depay, "state %d -> %d", old, state);
+
+ if (old == GST_RTP_WJ2_STATE_NONE || old == state) {
+ /* we're at the desired state, all fine */
+ } else if (state == GST_RTP_WJ2_STATE_WHEADER) {
+ if (old == GST_RTP_WJ2_STATE_MHEADER) {
+ /* we found a wj2 header and we were doing Main headers,
+ * clear main headers */
+ gst_rtp_wj2_clear_adapter (rtpwj2depay, TRUE);
+ } else if (old == GST_RTP_WJ2_STATE_MDATA) {
+ /* we found a wj2 header and we were doing J2K data,
+ * flush the accumulated data */
+ }
+ } else if (state == GST_RTP_WJ2_STATE_MHEADER) {
+ if (old == GST_RTP_WJ2_STATE_WHEADER) {
+ /* we found a main header and we were doing wj2 headers,
+ * clear wj2 headers */
+ gst_rtp_wj2_clear_adapter (rtpwj2depay, TRUE);
+ } else if (old == GST_RTP_WJ2_STATE_MDATA) {
+ /* we found a main header and we were doing j2k data,
+ * flush data */
+ rtpwj2depay->offset = 0;
+ }
+ } else if (state == GST_RTP_WJ2_STATE_MDATA) {
+ if (old == GST_RTP_WJ2_STATE_WHEADER) {
+ /* we found j2k data and we were doing w2j headers,
+ * flush headers */
+ } else if (old == GST_RTP_WJ2_STATE_MHEADER) {
+ /* we found j2k data and we were doing main headers,
+ * flush headers */
+ }
+ }
+ rtpwj2depay->state = state;;
+}
+
+/* parse and store the WJ2 header packet */
+static void
+gst_rtp_wj2_handle_w_header (GstRtpWJ2Depay * rtpwj2depay, GstBuffer * buf,
+ guint8 * payload)
+{
+ GstBuffer *WH_buf;
+ guint offset, WHF, avail, WH_id;
+
+ /* first update our state */
+ gst_rtp_wj2_new_state (rtpwj2depay, GST_RTP_WJ2_STATE_WHEADER);
+
+ /* 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
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |1 1|WHF| WH_id | MBZ | fragment offset |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * WHF = 1 piece, 2 end, 3 complete
+ */
+ WHF = (payload[0] & 0x30) >> 4;
+ WH_id = (payload[0] & 0xf);
+ offset = (payload[2] << 8) | payload[3];
+
+ GST_DEBUG_OBJECT (rtpwj2depay, "WHF %u, WH_id %u, offset %u, expect %u",
+ WHF, WH_id, offset, rtpwj2depay->offset);
+
+ /* we have a valid WH_id if we didn't have a previous one of it it matched the
+ * previous one */
+ if (rtpwj2depay->WH_id == -1)
+ rtpwj2depay->WH_id = WH_id;
+ else if (rtpwj2depay->WH_id != WH_id)
+ goto unexpected_header;
+
+ /* valid offset when it matches our expected offset, we can't really generate
+ * dummy data for this header*/
+ if (rtpwj2depay->offset != offset)
+ goto unexpected_header;
+
+ /* take WJ2 header data, push in the adapter */
+ WH_buf = gst_rtp_buffer_get_payload_subbuffer (buf, 4, -1);
+ gst_adapter_push (rtpwj2depay->adapter, WH_buf);
+
+ /* adjust the expected next offset */
+ rtpwj2depay->offset += GST_BUFFER_SIZE (WH_buf);
+
+ if ((WHF & 2) == 2) {
+ /* we have a complete WJ2 header */
+ avail = gst_adapter_available (rtpwj2depay->adapter);
+ WH_buf = gst_adapter_take_buffer (rtpwj2depay->adapter, avail);
+
+ GST_LOG_OBJECT (rtpwj2depay, "complete header %u of size %u", WH_id, avail);
+
+ /* store the new header */
+ gst_rtp_wj2_store_wheader (rtpwj2depay, WH_id, WH_buf);
+ }
+ return;
+
+ /* ERRORS */
+unexpected_header:
+ {
+ /* doesn't match, we need to discard data */
+ gst_rtp_wj2_clear_adapter (rtpwj2depay, TRUE);
+ return;
+ }
+}
+
+/* calculate how many tiles there are in the wj2 header */
+static void
+gst_rtp_wj2_ensure_ntiles (GstRtpWJ2Depay * rtpwj2depay, GstBuffer * buf)
+{
+ guint size, tiles;
+
+ size = GST_BUFFER_SIZE (buf);
+ if (size < 32)
+ return;
+
+ tiles = (size - 32) / 4;
+ GST_DEBUG_OBJECT (rtpwj2depay, "ensure %u tiles from size %u", tiles, size);
+ g_ptr_array_set_size (rtpwj2depay->tiles, tiles);
+}
+
+static GstBuffer *
+gst_rtp_wj2_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
+{
+ GstRtpWJ2Depay *rtpwj2depay;
+ guint8 *payload;
+ guint tp;
+ GstBuffer *out = NULL;
+
+ rtpwj2depay = GST_RTP_WJ2_DEPAY (depayload);
+
+ /* flush everything on discont for now */
+ if (GST_BUFFER_IS_DISCONT (buf)) {
+ if (rtpwj2depay->state != GST_RTP_WJ2_STATE_MDATA) {
+ GST_DEBUG_OBJECT (rtpwj2depay, "DISCONT, flushing data");
+ gst_rtp_wj2_clear_adapter (rtpwj2depay, TRUE);
+ } else {
+ GST_DEBUG_OBJECT (rtpwj2depay,
+ "DISCONT, not flushing data, we in data mode");
+ }
+ }
+
+ if (gst_rtp_buffer_get_payload_len (buf) < 8)
+ goto empty_packet;
+
+ payload = gst_rtp_buffer_get_payload (buf);
+
+ /* look at the header type */
+ tp = payload[0] >> 6;
+
+ GST_LOG_OBJECT (rtpwj2depay, "GOT tp %d", tp);
+
+ if (tp == 3) {
+ /* handle a WJ2 header packet */
+ gst_rtp_wj2_handle_w_header (rtpwj2depay, buf, payload);
+ } else {
+ guint MHF, L, offset, MH_id, WH_id, tci;
+ GstBuffer *J_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 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | MBZ |L| WH_id | fragment offset |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Tile Component index |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ MHF = (payload[0] & 0x30) >> 4;
+ MH_id = (payload[0] & 0xe) >> 1;
+ L = (payload[4] & 0x10) >> 4;
+ WH_id = payload[4] & 0xf;
+ offset = (payload[5] << 16) | (payload[6] << 8) | payload[7];
+ tci = (payload[8] << 8) | payload[9];
+
+ GST_DEBUG_OBJECT (rtpwj2depay,
+ "MHF %u, L %u, MH %u, WH %u, offset %u, TCI %u", MHF, L, MH_id, WH_id,
+ offset, tci);
+
+ GST_DEBUG_OBJECT (rtpwj2depay, "current WH_id %d, MH_id %d",
+ rtpwj2depay->WH_id, rtpwj2depay->MH_id);
+
+ if (MHF != 0) {
+ gst_rtp_wj2_new_state (rtpwj2depay, GST_RTP_WJ2_STATE_MHEADER);
+
+ /* we have a Main JPEG 2000 header */
+ GST_DEBUG_OBJECT (rtpwj2depay,
+ "we have a main header, expected offset %u", rtpwj2depay->offset);
+ /* valid offset when it matches our expected offset, we can't really generate
+ * dummy data for this header*/
+ if (rtpwj2depay->offset != offset)
+ goto invalid_m_header;
+ } else {
+ GstBuffer *mheader = NULL;
+
+ gst_rtp_wj2_new_state (rtpwj2depay, GST_RTP_WJ2_STATE_MDATA);
+
+ GST_DEBUG_OBJECT (rtpwj2depay,
+ "we have JPEG2000 data, expected offset %u", rtpwj2depay->offset);
+
+ /* check if we have the WJ2 header */
+ if (rtpwj2depay->WH[WH_id] == NULL)
+ goto waiting_header;
+
+ if (MH_id != 0) {
+ /* check if we have the main header */
+ if ((mheader = rtpwj2depay->MH[MH_id]) == NULL)
+ goto waiting_header;
+ } else if (rtpwj2depay->MH_id == -1)
+ goto waiting_header;
+
+ if (rtpwj2depay->offset != offset) {
+ guint fillsize;
+
+ if (offset < rtpwj2depay->offset)
+ goto invalid_packet;
+
+ fillsize = offset - rtpwj2depay->offset;
+
+ /* gap in the data, this could either be a missing header or a gap in the
+ * data, check for missing header first */
+ if (mheader && fillsize == GST_BUFFER_SIZE (mheader)) {
+ GST_DEBUG_OBJECT (rtpwj2depay, "missing main header of %u bytes",
+ fillsize);
+ /* push header in the adapter */
+ gst_adapter_push (rtpwj2depay->adapter, mheader);
+ } else {
+ /* fill with 0 */
+ GST_DEBUG_OBJECT (rtpwj2depay, "filling gap of %u bytes", fillsize);
+
+ J_buf = gst_buffer_new_and_alloc (fillsize);
+ gst_adapter_push (rtpwj2depay->adapter, J_buf);
+ }
+ }
+ }
+
+ if (rtpwj2depay->WH_id == -1) {
+ /* first time we lock onto a WH_id */
+ rtpwj2depay->WH_id = WH_id;
+ gst_rtp_wj2_ensure_ntiles (rtpwj2depay, rtpwj2depay->WH[WH_id]);
+ } else if (rtpwj2depay->WH_id != WH_id)
+ goto invalid_m_header;
+
+ /* we have a valid MH_id if we didn't have a previous one or if it matched the
+ * previous one */
+ if (rtpwj2depay->MH_id == -1)
+ rtpwj2depay->MH_id = MH_id;
+ else if (rtpwj2depay->MH_id != MH_id)
+ goto invalid_m_header;
+
+ /* take JPEG2000 data, push in the adapter */
+ J_buf = gst_rtp_buffer_get_payload_subbuffer (buf, 10, -1);
+ GST_DEBUG_OBJECT (rtpwj2depay, "adding buffer of %u bytes",
+ GST_BUFFER_SIZE (J_buf));
+ rtpwj2depay->offset += GST_BUFFER_SIZE (J_buf);
+ gst_adapter_push (rtpwj2depay->adapter, J_buf);
+
+ if (MHF != 0) {
+ if ((MHF & 2) == 2) {
+ guint avail;
+
+ /* we have a complete JPEG2000 header */
+ avail = gst_adapter_available (rtpwj2depay->adapter);
+ GST_LOG_OBJECT (rtpwj2depay, "complete main header %u of size %u",
+ MH_id, avail);
+
+ /* only cache headers when != 0 */
+ if (MH_id != 0) {
+ J_buf = gst_adapter_take_buffer (rtpwj2depay->adapter, avail);
+ /* store the new header */
+ gst_rtp_wj2_store_mheader (rtpwj2depay, MH_id, J_buf);
+ /* and push it back in */
+ gst_adapter_push (rtpwj2depay->adapter, J_buf);
+ }
+ }
+ } else {
+ /* we have a complete JPEG2000 packet */
+ if (L) {
+ guint avail;
+
+ avail = gst_adapter_available (rtpwj2depay->adapter);
+ J_buf = gst_adapter_take_buffer (rtpwj2depay->adapter, avail);
+
+ GST_LOG_OBJECT (rtpwj2depay, "complete data of size %u", avail);
+
+ gst_rtp_wj2_store_tile (rtpwj2depay, tci, J_buf);
+ }
+ }
+
+ /* last packet, construct WJ2 image from collected data */
+ if (gst_rtp_buffer_get_marker (buf)) {
+ GST_LOG_OBJECT (rtpwj2depay, "complete wj2 frame");
+ out = gst_rtp_wj2_flush_frame (rtpwj2depay);
+ }
+ }
+ return out;
+
+ /* ERRORS */
+empty_packet:
+ {
+ GST_ELEMENT_WARNING (rtpwj2depay, STREAM, DECODE,
+ ("Empty Payload."), (NULL));
+ return NULL;
+ }
+waiting_header:
+ {
+ GST_DEBUG_OBJECT (rtpwj2depay, "we are waiting for a header");
+ return NULL;
+ }
+invalid_m_header:
+ {
+ /* doesn't match, we need to discard data */
+ GST_DEBUG_OBJECT (rtpwj2depay, "invalid header");
+ gst_adapter_clear (rtpwj2depay->adapter);
+ rtpwj2depay->offset = 0;
+ rtpwj2depay->MH_id = -1;
+ rtpwj2depay->WH_id = -1;
+ return NULL;
+ }
+invalid_packet:
+ {
+ return NULL;
+ }
+}
+
+static GstStateChangeReturn
+gst_rtp_wj2_depay_change_state (GstElement * element, GstStateChange transition)
+{
+ GstRtpWJ2Depay *rtpwj2depay;
+ GstStateChangeReturn ret;
+
+ rtpwj2depay = GST_RTP_WJ2_DEPAY (element);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ break;
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ gst_rtp_wj2_clear_adapter (rtpwj2depay, TRUE);
+ break;
+ default:
+ break;
+ }
+
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ gst_rtp_wj2_clear_adapter (rtpwj2depay, TRUE);
+ break;
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+gboolean
+gst_rtp_wj2_depay_plugin_init (GstPlugin * plugin)
+{
+ return gst_element_register (plugin, "rtpwj2depay",
+ GST_RANK_MARGINAL, GST_TYPE_RTP_WJ2_DEPAY);
+}
diff --git a/gst/rtp/gstrtpwj2depay.h b/gst/rtp/gstrtpwj2depay.h
new file mode 100644
index 000000000..5b118d0cd
--- /dev/null
+++ b/gst/rtp/gstrtpwj2depay.h
@@ -0,0 +1,78 @@
+/* GStreamer
+ * Copyright (C) <2010> Wim Taymans <wim.taymans@gmail.com>
+ *
+ * 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_WJ2_DEPAY_H__
+#define __GST_RTP_WJ2_DEPAY_H__
+
+#include <gst/gst.h>
+#include <gst/base/gstadapter.h>
+#include <gst/rtp/gstbasertpdepayload.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_RTP_WJ2_DEPAY \
+ (gst_rtp_wj2_depay_get_type())
+#define GST_RTP_WJ2_DEPAY(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_WJ2_DEPAY,GstRtpWJ2Depay))
+#define GST_RTP_WJ2_DEPAY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_WJ2_DEPAY,GstRtpWJ2DepayClass))
+#define GST_IS_RTP_WJ2_DEPAY(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_WJ2_DEPAY))
+#define GST_IS_RTP_WJ2_DEPAY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_WJ2_DEPAY))
+
+typedef struct _GstRtpWJ2Depay GstRtpWJ2Depay;
+typedef struct _GstRtpWJ2DepayClass GstRtpWJ2DepayClass;
+
+typedef enum {
+ GST_RTP_WJ2_STATE_NONE,
+ GST_RTP_WJ2_STATE_WHEADER,
+ GST_RTP_WJ2_STATE_MHEADER,
+ GST_RTP_WJ2_STATE_MDATA
+} GstRtpWJ2State;
+
+struct _GstRtpWJ2Depay
+{
+ GstBaseRTPDepayload depayload;
+
+ GstBuffer *WH[16];
+ GstBuffer *MH[8];
+ GPtrArray *tiles;
+
+ gint WH_id;
+ gint MH_id;
+ guint offset;
+ GstAdapter *adapter;
+ GstRtpWJ2State state;
+
+ gint width, height;
+};
+
+struct _GstRtpWJ2DepayClass
+{
+ GstBaseRTPDepayloadClass parent_class;
+};
+
+GType gst_rtp_wj2_depay_get_type (void);
+
+gboolean gst_rtp_wj2_depay_plugin_init (GstPlugin * plugin);
+
+G_END_DECLS
+
+#endif /* __GST_RTP_WJ2_DEPAY_H__ */
diff --git a/gst/rtp/gstrtpwj2pay.c b/gst/rtp/gstrtpwj2pay.c
new file mode 100644
index 000000000..b5edaa156
--- /dev/null
+++ b/gst/rtp/gstrtpwj2pay.c
@@ -0,0 +1,526 @@
+/* GStreamer
+ * Copyright (C) 2010 Wim Taymans <wim.taymans@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * SECTION:element-rtpwj2pay
+ *
+ * Payload encode WJ2 pictures into RTP packets.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+#include <gst/rtp/gstrtpbuffer.h>
+
+#include "gstrtpwj2pay.h"
+
+static GstStaticPadTemplate gst_rtp_wj2_pay_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("image/x-wj2")
+ );
+
+static GstStaticPadTemplate gst_rtp_wj2_pay_src_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ 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) \"X-WJ2\"")
+ );
+
+GST_DEBUG_CATEGORY_STATIC (rtpwj2pay_debug);
+#define GST_CAT_DEFAULT (rtpwj2pay_debug)
+
+/*
+ * RtpWJ2Marker:
+ * @WJ2_MARKER: Prefix for JPEG 2000 marker
+ * @WJ2_MARKER_SOC: Start of Codestream
+ * @WJ2_MARKER_SOT: Start of tile
+ * @WJ2_MARKER_EOC: End of Codestream
+ *
+ * Identifers for markers in JPEG 2000 codestreams
+ */
+typedef enum
+{
+ WJ2_MARKER = 0xFF,
+ WJ2_MARKER_SOC = 0x4F,
+ WJ2_MARKER_SOT = 0x90,
+ WJ2_MARKER_EOC = 0xD9
+} RtpWJ2Marker;
+
+enum
+{
+ PROP_0,
+ PROP_LAST
+};
+
+typedef struct
+{
+ guint tp:2;
+ guint MHF:2;
+ guint mh_id:3;
+ guint T:1;
+ guint priority:8;
+ guint tile:16;
+ guint L:1;
+ guint WH_id:4;
+ guint offset:24;
+ guint tci:16;
+} RtpWJ2Header;
+
+static gboolean gst_rtp_wj2_pay_setcaps (GstBaseRTPPayload * basepayload,
+ GstCaps * caps);
+
+static GstFlowReturn gst_rtp_wj2_pay_handle_buffer (GstBaseRTPPayload * pad,
+ GstBuffer * buffer);
+
+GST_BOILERPLATE (GstRtpWJ2Pay, gst_rtp_wj2_pay, GstBaseRTPPayload,
+ GST_TYPE_BASE_RTP_PAYLOAD);
+
+static void
+gst_rtp_wj2_pay_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_wj2_pay_src_template));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&gst_rtp_wj2_pay_sink_template));
+
+ gst_element_class_set_details_simple (element_class,
+ "RTP WJ2 payloader", "Codec/Payloader/Network",
+ "Payload-encodes WJ2 pictures into RTP packets",
+ "Wim Taymans <wim.taymans@gmail.com>");
+}
+
+static void
+gst_rtp_wj2_pay_class_init (GstRtpWJ2PayClass * klass)
+{
+ GstBaseRTPPayloadClass *gstbasertppayload_class;
+
+ gstbasertppayload_class = (GstBaseRTPPayloadClass *) klass;
+
+ gstbasertppayload_class->set_caps = gst_rtp_wj2_pay_setcaps;
+ gstbasertppayload_class->handle_buffer = gst_rtp_wj2_pay_handle_buffer;
+
+ GST_DEBUG_CATEGORY_INIT (rtpwj2pay_debug, "rtpwj2pay", 0,
+ "WJ2 RTP Payloader");
+}
+
+static void
+gst_rtp_wj2_pay_init (GstRtpWJ2Pay * pay, GstRtpWJ2PayClass * klass)
+{
+}
+
+static gboolean
+gst_rtp_wj2_pay_setcaps (GstBaseRTPPayload * basepayload, GstCaps * caps)
+{
+ GstStructure *caps_structure = gst_caps_get_structure (caps, 0);
+ GstRtpWJ2Pay *pay;
+ gint width = 0, height = 0;
+ gboolean res;
+
+ pay = GST_RTP_WJ2_PAY (basepayload);
+
+ /* these properties are not mandatory, we can get them from the stream */
+ if (gst_structure_get_int (caps_structure, "height", &height)) {
+ pay->height = height;
+ }
+ if (gst_structure_get_int (caps_structure, "width", &width)) {
+ pay->width = width;
+ }
+
+ gst_basertppayload_set_options (basepayload, "video", TRUE, "X-WJ2", 90000);
+ res = gst_basertppayload_set_outcaps (basepayload, NULL);
+
+ return res;
+}
+
+
+static guint
+gst_rtp_wj2_pay_header_size (const guint8 * data, guint offset)
+{
+ return data[offset] << 8 | data[offset + 1];
+}
+
+static RtpWJ2Marker
+gst_rtp_wj2_pay_scan_marker (const guint8 * data, guint size, guint * offset)
+{
+ while ((data[(*offset)++] != WJ2_MARKER) && ((*offset) < size));
+
+ if (G_UNLIKELY ((*offset) >= size)) {
+ GST_LOG ("end of data, return EOC");
+ return WJ2_MARKER_EOC;
+ } else {
+ guint8 marker = data[(*offset)++];
+ GST_LOG ("found %02x marker", marker);
+ return marker;
+ }
+}
+
+static guint
+find_pu_end (GstRtpWJ2Pay * pay, const guint8 * data, guint size,
+ guint offset, RtpWJ2Header * header)
+{
+ /* parse the wj2 header for 'start of codestream' */
+ while (offset < size) {
+ GST_LOG_OBJECT (pay, "checking from offset %u", offset);
+ switch (gst_rtp_wj2_pay_scan_marker (data, size, &offset)) {
+ case WJ2_MARKER_SOC:
+ GST_DEBUG_OBJECT (pay, "found SOC at %u", offset);
+ header->MHF = 1;
+ break;
+ case WJ2_MARKER_SOT:
+ {
+ guint len, Psot;
+
+ GST_DEBUG_OBJECT (pay, "found SOT at %u", offset);
+ /* we found SOT but also had a header first */
+ if (header->MHF)
+ return offset - 2;
+
+ /* parse SOT but do some sanity checks first */
+ len = gst_rtp_wj2_pay_header_size (data, offset);
+ GST_DEBUG_OBJECT (pay, "SOT length %u", len);
+ if (len < 8)
+ return size;
+ if (offset + len >= size)
+ return size;
+
+ /* we have a valid tile number now, keep it in the header */
+ header->T = 0;
+ header->tile = GST_READ_UINT16_BE (&data[offset + 2]);
+
+ /* get offset of next tile, if it's 0, it goes all the way to the end of
+ * the data */
+ Psot = GST_READ_UINT32_BE (&data[offset + 4]);
+ if (Psot == 0)
+ offset = size;
+ else
+ offset += Psot;
+ GST_DEBUG_OBJECT (pay, "Isot %u, Psot %u", header->tile, Psot);
+ break;
+ }
+ case WJ2_MARKER_EOC:
+ GST_DEBUG_OBJECT (pay, "found EOC");
+ return offset;
+ default:
+ offset += gst_rtp_wj2_pay_header_size (data, offset);
+ break;
+ }
+ }
+ GST_DEBUG_OBJECT (pay, "reached end of data");
+ return size;
+}
+
+static GstFlowReturn
+gst_rtp_wj2_pay_handle_buffer (GstBaseRTPPayload * basepayload,
+ GstBuffer * buffer)
+{
+ GstRtpWJ2Pay *pay;
+ GstClockTime timestamp;
+ RtpWJ2Header wj2_header;
+ GstFlowReturn ret = GST_FLOW_ERROR;
+ guint8 *data;
+ guint size, i, WH_id;
+ guint mtu;
+ guint offset, length;
+ guint T_count, C_count, TC_count;
+ guint packet_size, header_size;
+ GstBuffer *outbuf;
+ guint8 *payload;
+ guint payload_size;
+ guint Xsiz, Ysiz, XOsiz, YOsiz, XTsiz, YTsiz, XTOsiz, YTOsiz;
+
+ pay = GST_RTP_WJ2_PAY (basepayload);
+ mtu = GST_BASE_RTP_PAYLOAD_MTU (pay);
+
+ size = GST_BUFFER_SIZE (buffer);
+ data = GST_BUFFER_DATA (buffer);
+ timestamp = GST_BUFFER_TIMESTAMP (buffer);
+
+ GST_LOG_OBJECT (pay, "got buffer size %u, timestamp %" GST_TIME_FORMAT, size,
+ GST_TIME_ARGS (timestamp));
+
+ /* start with contructing the header */
+ if (size < 42)
+ goto too_small;
+
+ /* get tile count */
+ T_count = data[40];
+ /* get component count */
+ C_count = data[41];
+ /* number of tiles/components */
+ TC_count = T_count * C_count;
+ GST_LOG_OBJECT (pay, "T %d, C %d, CT %d", T_count, C_count, TC_count);
+
+ /* calculate header size, 36 bytes for the fixed header size and 4 bytes for
+ * each tile/component info */
+ header_size = 36 + (TC_count * 4);
+
+ packet_size = gst_rtp_buffer_calc_packet_len (header_size, 0, 0);
+ GST_DEBUG_OBJECT (pay, "needed packet size %u", packet_size);
+
+ if (packet_size > mtu)
+ goto too_large;
+
+ outbuf = gst_rtp_buffer_new_allocate_len (packet_size, 0, 0);
+ GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
+
+ /* get pointer to header and size of the payload */
+ payload = gst_rtp_buffer_get_payload (outbuf);
+ payload_size = gst_rtp_buffer_get_payload_len (outbuf);
+
+ GST_DEBUG_OBJECT (pay, "got payload_size %u", payload_size);
+
+ WH_id = 0;
+ Xsiz = GST_READ_UINT32_LE (data + 8);
+ Ysiz = GST_READ_UINT32_LE (data + 12);
+ XOsiz = GST_READ_UINT32_LE (data + 16);
+ YOsiz = GST_READ_UINT32_LE (data + 20);
+ XTsiz = GST_READ_UINT32_LE (data + 24);
+ YTsiz = GST_READ_UINT32_LE (data + 28);
+ XTOsiz = GST_READ_UINT32_LE (data + 32);
+ YTOsiz = GST_READ_UINT32_LE (data + 36);
+
+ GST_DEBUG_OBJECT (pay, "Xsiz %u, Ysiz %u, XOsiz %u, YOsiz %u", Xsiz, Ysiz,
+ XOsiz, YOsiz);
+ GST_DEBUG_OBJECT (pay, "XTsiz %u, YTsiz %u, XTOsiz %u, YTOsiz %u", XTsiz,
+ YTsiz, XTOsiz, YTOsiz);
+
+ /* 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
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |1 1|WHF| WH_id | MBZ | fragment offset |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Xsiz |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Ysiz |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | XOsiz |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | YOsiz |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | XTsiz |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | YTsiz |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | XTOsiz |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | YTOsiz |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | CT Info 0 .. |
+ * . ... .
+ * | CT Info (TC count - 1) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ payload[0] = 0xf0 | WH_id;
+ payload[1] = 0;
+ payload[2] = 0;
+ payload[3] = 0;
+ GST_WRITE_UINT32_BE (payload + 4, Xsiz);
+ GST_WRITE_UINT32_BE (payload + 8, Ysiz);
+ GST_WRITE_UINT32_BE (payload + 12, XOsiz);
+ GST_WRITE_UINT32_BE (payload + 16, YOsiz);
+ GST_WRITE_UINT32_BE (payload + 20, XTsiz);
+ GST_WRITE_UINT32_BE (payload + 24, YTsiz);
+ GST_WRITE_UINT32_BE (payload + 28, XTOsiz);
+ GST_WRITE_UINT32_BE (payload + 32, YTOsiz);
+
+ for (i = 0; i < TC_count; i++) {
+ guint32 comp_info;
+ guint CT, COD_STYLE, VFORMAT, version;
+ /*
+ * 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 | CT | COD_STYLE | VFORMAT | version |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ CT = data[74 + (9 * i)];
+ offset = GST_READ_UINT32_LE (&data[75 + (9 * i)]);
+ length = GST_READ_UINT32_LE (&data[79 + (9 * i)]);
+ GST_DEBUG_OBJECT (pay, "offset %02d %u, length %u", i, offset, length);
+
+ if (offset + 11 >= size)
+ goto too_small;
+
+ /* read info from the ADV202 header */
+ COD_STYLE = data[offset + 8];
+ VFORMAT = data[offset + 9];
+ version = data[offset + 11];
+
+ comp_info = (CT << 24) | (COD_STYLE << 16) | (VFORMAT << 8) | version;
+ GST_DEBUG_OBJECT (pay, "comp_info %02d %08x, CT %d, COD %02x, VF %d", i,
+ comp_info, CT, COD_STYLE, VFORMAT);
+
+ GST_WRITE_UINT32_BE (&payload[36 + (4 * i)], comp_info);
+ }
+
+ ret = gst_basertppayload_push (basepayload, outbuf);
+ if (ret != GST_FLOW_OK)
+ goto done;
+
+ /* do some header defaults first */
+ wj2_header.WH_id = 0; /* always 0 for now */
+
+ for (i = 0; i < TC_count; i++) {
+ guint8 *jdata;
+ guint jsize;
+
+ /* fill in tile component index */
+ wj2_header.tci = i;
+ wj2_header.L = 0;
+
+ /* move to the beginning of the ADV202 header */
+ jdata = data + GST_READ_UINT32_LE (&data[75 + (9 * i)]);
+ jsize = GST_READ_UINT32_LE (&data[79 + (9 * i)]);
+ /* skip ADV header */
+ jdata += 16;
+ jsize -= 16;
+ offset = 0;
+
+ wj2_header.tp = 0; /* only progressive scan */
+ wj2_header.MHF = 0; /* no header */
+ wj2_header.mh_id = 0; /* always 0 for now */
+ wj2_header.T = 1; /* invalid tile */
+ wj2_header.priority = 255; /* always 255 for now */
+ wj2_header.tile = 0; /* no tile number */
+
+ do {
+ GstBuffer *outbuf;
+ guint8 *payload, *header;
+ guint payload_size;
+ guint pu_size, end;
+
+ /* scan next packetization unit and fill in the header */
+ end = find_pu_end (pay, jdata, jsize, offset, &wj2_header);
+ pu_size = end - offset;
+
+ GST_DEBUG_OBJECT (pay, "pu of size %u", pu_size);
+
+ while (pu_size > 0) {
+
+ /* calculate the packet size */
+ packet_size = gst_rtp_buffer_calc_packet_len (pu_size + 10, 0, 0);
+
+ GST_DEBUG_OBJECT (pay, "packet size %u, offset %u", packet_size,
+ offset);
+
+ /* make sure it fits the MTU */
+ packet_size = (packet_size < mtu ? packet_size : mtu);
+ outbuf = gst_rtp_buffer_new_allocate_len (packet_size, 0, 0);
+ GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
+
+ /* get pointer to header and size of the payload */
+ header = gst_rtp_buffer_get_payload (outbuf);
+ payload_size = gst_rtp_buffer_get_payload_len (outbuf);
+
+ /* skip header and move to the payload */
+ payload = header + 10;
+ payload_size -= 10;
+
+ /* copy payload */
+ memcpy (payload, &jdata[offset], payload_size);
+
+ pu_size -= payload_size;
+ if (pu_size == 0) {
+ /* reached the end of a packetization unit */
+ if (wj2_header.MHF) {
+ /* we were doing a header, see if all fit in one packet or if
+ * we had to fragment it */
+ if (offset == 0)
+ wj2_header.MHF = 3;
+ else
+ wj2_header.MHF = 2;
+ }
+ if (end >= jsize) {
+ /* end of TC */
+ wj2_header.L = 1;
+ /* end of WJ2 image */
+ if (i == (TC_count - 1))
+ gst_rtp_buffer_set_marker (outbuf, TRUE);
+ }
+ }
+
+ wj2_header.offset = offset;
+ /* 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 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | MBZ |L| WH_id | fragment offset |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Tile Component index |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ header[0] = (wj2_header.tp << 6) | (wj2_header.MHF << 4) |
+ (wj2_header.mh_id << 1) | wj2_header.T;
+ header[1] = wj2_header.priority;
+ header[2] = wj2_header.tile >> 8;
+ header[3] = wj2_header.tile & 0xff;
+ header[4] = (wj2_header.L << 4) | (wj2_header.WH_id & 0xf);
+ header[5] = wj2_header.offset >> 16;
+ header[6] = (wj2_header.offset >> 8) & 0xff;
+ header[7] = wj2_header.offset & 0xff;
+ header[8] = (wj2_header.tci >> 8) & 0xff;
+ header[9] = wj2_header.tci & 0xff;
+
+ ret = gst_basertppayload_push (basepayload, outbuf);
+ if (ret != GST_FLOW_OK)
+ goto done;
+
+ /* reset header for next round */
+ wj2_header.MHF = 0;
+ wj2_header.T = 1;
+ wj2_header.tile = 0;
+
+ offset += payload_size;
+ }
+ offset = end;
+ } while (offset < jsize);
+ }
+
+done:
+ gst_buffer_unref (buffer);
+
+ return ret;
+
+ /* ERRORS */
+too_small:
+ {
+ return GST_FLOW_OK;
+ }
+too_large:
+ {
+ return GST_FLOW_OK;
+ }
+}
+
+gboolean
+gst_rtp_wj2_pay_plugin_init (GstPlugin * plugin)
+{
+ return gst_element_register (plugin, "rtpwj2pay", GST_RANK_NONE,
+ GST_TYPE_RTP_WJ2_PAY);
+}
diff --git a/gst/rtp/gstrtpwj2pay.h b/gst/rtp/gstrtpwj2pay.h
new file mode 100644
index 000000000..153e1630e
--- /dev/null
+++ b/gst/rtp/gstrtpwj2pay.h
@@ -0,0 +1,61 @@
+/* GStreamer
+ * Copyright (C) 2010 Wim Taymans <wim.taymans@gmail.com>
+ *
+ * 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_WJ2_PAY_H__
+#define __GST_RTP_WJ2_PAY_H__
+
+#include <gst/gst.h>
+#include <gst/rtp/gstbasertppayload.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_RTP_WJ2_PAY \
+ (gst_rtp_wj2_pay_get_type())
+#define GST_RTP_WJ2_PAY(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_WJ2_PAY,GstRtpWJ2Pay))
+#define GST_RTP_WJ2_PAY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_WJ2_PAY,GstRtpWJ2PayClass))
+#define GST_IS_RTP_WJ2_PAY(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_WJ2_PAY))
+#define GST_IS_RTP_WJ2_PAY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_WJ2_PAY))
+
+typedef struct _GstRtpWJ2Pay GstRtpWJ2Pay;
+typedef struct _GstRtpWJ2PayClass GstRtpWJ2PayClass;
+
+struct _GstRtpWJ2Pay
+{
+ GstBaseRTPPayload payload;
+
+ gint height;
+ gint width;
+};
+
+struct _GstRtpWJ2PayClass
+{
+ GstBaseRTPPayloadClass parent_class;
+};
+
+GType gst_rtp_wj2_pay_get_type (void);
+
+gboolean gst_rtp_wj2_pay_plugin_init (GstPlugin * plugin);
+
+G_END_DECLS
+
+#endif /* __GST_RTP_WJ2_PAY_H__ */