ee4da03086c62064f55eddf0a4b79b56213bb5be
[platform/upstream/gst-plugins-good.git] / gst / rtp / gstrtpj2kpay.c
1 /* GStreamer
2  * Copyright (C) 2009 Wim Taymans <wim.taymans@gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 /**
21  * SECTION:element-rtpj2kpay
22  *
23  * Payload encode JPEG 2000 pictures into RTP packets according to RFC 5371.
24  * For detailed information see: http://www.rfc-editor.org/rfc/rfc5371.txt
25  *
26  * The payloader takes a JPEG 2000 picture, scans the header for packetization
27  * units and constructs the RTP packet header followed by the actual JPEG 2000
28  * codestream.
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #  include "config.h"
33 #endif
34
35 #include <string.h>
36 #include <gst/rtp/gstrtpbuffer.h>
37 #include <gst/video/video.h>
38
39 #include "gstrtpj2kpay.h"
40 #include "gstrtputils.h"
41
42 static GstStaticPadTemplate gst_rtp_j2k_pay_sink_template =
43 GST_STATIC_PAD_TEMPLATE ("sink",
44     GST_PAD_SINK,
45     GST_PAD_ALWAYS,
46     GST_STATIC_CAPS ("image/x-jpc")
47     );
48
49 static GstStaticPadTemplate gst_rtp_j2k_pay_src_template =
50 GST_STATIC_PAD_TEMPLATE ("src",
51     GST_PAD_SRC,
52     GST_PAD_ALWAYS,
53     GST_STATIC_CAPS ("application/x-rtp, "
54         "  media = (string) \"video\", "
55         "  payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
56         "  clock-rate = (int) 90000, "
57         "  encoding-name = (string) \"JPEG2000\"")
58     );
59
60 GST_DEBUG_CATEGORY_STATIC (rtpj2kpay_debug);
61 #define GST_CAT_DEFAULT (rtpj2kpay_debug)
62
63 /*
64  * RtpJ2KMarker:
65  * @J2K_MARKER: Prefix for JPEG 2000 marker
66  * @J2K_MARKER_SOC: Start of Codestream
67  * @J2K_MARKER_SOT: Start of tile
68  * @J2K_MARKER_EOC: End of Codestream
69  *
70  * Identifers for markers in JPEG 2000 codestreams
71  */
72 typedef enum
73 {
74   J2K_MARKER = 0xFF,
75   J2K_MARKER_SOC = 0x4F,
76   J2K_MARKER_SOT = 0x90,
77   J2K_MARKER_SOP = 0x91,
78   J2K_MARKER_EPH = 0x92,
79   J2K_MARKER_SOD = 0x93,
80   J2K_MARKER_EOC = 0xD9
81 } RtpJ2KMarker;
82
83 enum
84 {
85   PROP_0,
86   PROP_LAST
87 };
88
89 typedef struct
90 {
91   guint tp:2;
92   guint MHF:2;
93   guint mh_id:3;
94   guint T:1;
95   guint priority:8;
96   guint tile:16;
97   guint offset:24;
98 } RtpJ2KHeader;
99
100 #define HEADER_SIZE 8
101
102 static void gst_rtp_j2k_pay_set_property (GObject * object, guint prop_id,
103     const GValue * value, GParamSpec * pspec);
104 static void gst_rtp_j2k_pay_get_property (GObject * object, guint prop_id,
105     GValue * value, GParamSpec * pspec);
106
107 static gboolean gst_rtp_j2k_pay_setcaps (GstRTPBasePayload * basepayload,
108     GstCaps * caps);
109
110 static GstFlowReturn gst_rtp_j2k_pay_handle_buffer (GstRTPBasePayload * pad,
111     GstBuffer * buffer);
112
113 #define gst_rtp_j2k_pay_parent_class parent_class
114 G_DEFINE_TYPE (GstRtpJ2KPay, gst_rtp_j2k_pay, GST_TYPE_RTP_BASE_PAYLOAD);
115
116 static void
117 gst_rtp_j2k_pay_class_init (GstRtpJ2KPayClass * klass)
118 {
119   GObjectClass *gobject_class;
120   GstElementClass *gstelement_class;
121   GstRTPBasePayloadClass *gstrtpbasepayload_class;
122
123   gobject_class = (GObjectClass *) klass;
124   gstelement_class = (GstElementClass *) klass;
125   gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass;
126
127   gobject_class->set_property = gst_rtp_j2k_pay_set_property;
128   gobject_class->get_property = gst_rtp_j2k_pay_get_property;
129
130   gst_element_class_add_static_pad_template (gstelement_class,
131       &gst_rtp_j2k_pay_src_template);
132   gst_element_class_add_static_pad_template (gstelement_class,
133       &gst_rtp_j2k_pay_sink_template);
134
135   gst_element_class_set_static_metadata (gstelement_class,
136       "RTP JPEG 2000 payloader", "Codec/Payloader/Network/RTP",
137       "Payload-encodes JPEG 2000 pictures into RTP packets (RFC 5371)",
138       "Wim Taymans <wim.taymans@gmail.com>");
139
140   gstrtpbasepayload_class->set_caps = gst_rtp_j2k_pay_setcaps;
141   gstrtpbasepayload_class->handle_buffer = gst_rtp_j2k_pay_handle_buffer;
142
143   GST_DEBUG_CATEGORY_INIT (rtpj2kpay_debug, "rtpj2kpay", 0,
144       "JPEG 2000 RTP Payloader");
145 }
146
147 static void
148 gst_rtp_j2k_pay_init (GstRtpJ2KPay * pay)
149 {
150 }
151
152 static gboolean
153 gst_rtp_j2k_pay_setcaps (GstRTPBasePayload * basepayload, GstCaps * caps)
154 {
155   GstStructure *caps_structure = gst_caps_get_structure (caps, 0);
156   GstRtpJ2KPay *pay;
157   gint width = 0, height = 0;
158   gboolean res;
159
160   pay = GST_RTP_J2K_PAY (basepayload);
161
162   /* these properties are not mandatory, we can get them from the stream */
163   if (gst_structure_get_int (caps_structure, "height", &height)) {
164     pay->height = height;
165   }
166   if (gst_structure_get_int (caps_structure, "width", &width)) {
167     pay->width = width;
168   }
169
170   gst_rtp_base_payload_set_options (basepayload, "video", TRUE, "JPEG2000",
171       90000);
172   res = gst_rtp_base_payload_set_outcaps (basepayload, NULL);
173
174   return res;
175 }
176
177
178 static guint
179 gst_rtp_j2k_pay_header_size (const guint8 * data, guint offset)
180 {
181   return data[offset] << 8 | data[offset + 1];
182 }
183
184 static RtpJ2KMarker
185 gst_rtp_j2k_pay_scan_marker (const guint8 * data, guint size, guint * offset)
186 {
187   while ((data[(*offset)++] != J2K_MARKER) && ((*offset) < size));
188
189   if (G_UNLIKELY ((*offset) >= size)) {
190     return J2K_MARKER_EOC;
191   } else {
192     guint8 marker = data[(*offset)++];
193     return marker;
194   }
195 }
196
197 typedef struct
198 {
199   RtpJ2KHeader header;
200   gboolean bitstream;
201   guint n_tiles;
202   guint next_sot;
203   gboolean force_packet;
204 } RtpJ2KState;
205
206 static guint
207 find_pu_end (GstRtpJ2KPay * pay, const guint8 * data, guint size,
208     guint offset, RtpJ2KState * state)
209 {
210   gboolean cut_sop = FALSE;
211   RtpJ2KMarker marker;
212
213   /* parse the j2k header for 'start of codestream' */
214   GST_LOG_OBJECT (pay, "checking from offset %u", offset);
215   while (offset < size) {
216     marker = gst_rtp_j2k_pay_scan_marker (data, size, &offset);
217
218     if (state->bitstream) {
219       /* parsing bitstream, only look for SOP */
220       switch (marker) {
221         case J2K_MARKER_SOP:
222           GST_LOG_OBJECT (pay, "found SOP at %u", offset);
223           if (cut_sop)
224             return offset - 2;
225           cut_sop = TRUE;
226           break;
227         case J2K_MARKER_EPH:
228           /* just skip over EPH */
229           GST_LOG_OBJECT (pay, "found EPH at %u", offset);
230           break;
231         default:
232           if (offset >= state->next_sot) {
233             GST_LOG_OBJECT (pay, "reached next SOT at %u", offset);
234             state->bitstream = FALSE;
235             state->force_packet = TRUE;
236             if (marker == J2K_MARKER_EOC && state->next_sot + 2 <= size)
237               /* include EOC but never go past the max size */
238               return state->next_sot + 2;
239             else
240               return state->next_sot;
241           }
242           break;
243       }
244     } else {
245       switch (marker) {
246         case J2K_MARKER_SOC:
247           GST_LOG_OBJECT (pay, "found SOC at %u", offset);
248           state->header.MHF = 1;
249           break;
250         case J2K_MARKER_SOT:
251         {
252           guint len, Psot;
253
254           GST_LOG_OBJECT (pay, "found SOT at %u", offset);
255           /* we found SOT but also had a header first */
256           if (state->header.MHF) {
257             state->force_packet = TRUE;
258             return offset - 2;
259           }
260
261           /* parse SOT but do some sanity checks first */
262           len = gst_rtp_j2k_pay_header_size (data, offset);
263           GST_LOG_OBJECT (pay, "SOT length %u", len);
264           if (len < 8)
265             return size;
266           if (offset + len >= size)
267             return size;
268
269           if (state->n_tiles == 0)
270             /* first tile, T is valid */
271             state->header.T = 0;
272           else
273             /* more tiles, T becomes invalid */
274             state->header.T = 1;
275           state->header.tile = GST_READ_UINT16_BE (&data[offset + 2]);
276           state->n_tiles++;
277
278           /* get offset of next tile, if it's 0, it goes all the way to the end of
279            * the data */
280           Psot = GST_READ_UINT32_BE (&data[offset + 4]);
281           if (Psot == 0)
282             state->next_sot = size;
283           else
284             state->next_sot = offset - 2 + Psot;
285
286           offset += len;
287           GST_LOG_OBJECT (pay, "Isot %u, Psot %u, next %u", state->header.tile,
288               Psot, state->next_sot);
289           break;
290         }
291         case J2K_MARKER_SOD:
292           GST_LOG_OBJECT (pay, "found SOD at %u", offset);
293           /* can't have more tiles now */
294           state->n_tiles = 0;
295           /* go to bitstream parsing */
296           state->bitstream = TRUE;
297           /* cut at the next SOP or else include all data */
298           cut_sop = TRUE;
299           /* force a new packet when we see SOP, this can be optional but the
300            * spec recommends packing headers separately */
301           state->force_packet = TRUE;
302           break;
303         case J2K_MARKER_EOC:
304           GST_LOG_OBJECT (pay, "found EOC at %u", offset);
305           return offset;
306         default:
307         {
308           guint len = gst_rtp_j2k_pay_header_size (data, offset);
309           GST_LOG_OBJECT (pay, "skip 0x%02x len %u", marker, len);
310           offset += len;
311           break;
312         }
313       }
314     }
315   }
316   GST_DEBUG_OBJECT (pay, "reached end of data");
317   return size;
318 }
319
320 static GstFlowReturn
321 gst_rtp_j2k_pay_handle_buffer (GstRTPBasePayload * basepayload,
322     GstBuffer * buffer)
323 {
324   GstRtpJ2KPay *pay;
325   GstClockTime timestamp;
326   GstFlowReturn ret = GST_FLOW_ERROR;
327   RtpJ2KState state;
328   GstBufferList *list = NULL;
329   GstMapInfo map;
330   guint mtu, max_size;
331   guint offset;
332   guint end, pos;
333
334   pay = GST_RTP_J2K_PAY (basepayload);
335   mtu = GST_RTP_BASE_PAYLOAD_MTU (pay);
336
337   gst_buffer_map (buffer, &map, GST_MAP_READ);
338   timestamp = GST_BUFFER_PTS (buffer);
339   offset = pos = end = 0;
340
341   GST_LOG_OBJECT (pay,
342       "got buffer size %" G_GSIZE_FORMAT ", timestamp %" GST_TIME_FORMAT,
343       map.size, GST_TIME_ARGS (timestamp));
344
345   /* do some header defaults first */
346   state.header.tp = 0;          /* only progressive scan */
347   state.header.MHF = 0;         /* no header */
348   state.header.mh_id = 0;       /* always 0 for now */
349   state.header.T = 1;           /* invalid tile */
350   state.header.priority = 255;  /* always 255 for now */
351   state.header.tile = 0;        /* no tile number */
352   state.header.offset = 0;      /* offset of 0 */
353   state.bitstream = FALSE;
354   state.n_tiles = 0;
355   state.next_sot = 0;
356   state.force_packet = FALSE;
357
358   /* get max packet length */
359   max_size = gst_rtp_buffer_calc_payload_len (mtu - HEADER_SIZE, 0, 0);
360
361   list = gst_buffer_list_new_sized ((mtu / max_size) + 1);
362
363   do {
364     GstBuffer *outbuf;
365     guint8 *header;
366     guint payload_size;
367     guint pu_size;
368     GstRTPBuffer rtp = { NULL };
369
370     /* try to pack as much as we can */
371     do {
372       /* see how much we have scanned already */
373       pu_size = end - offset;
374       GST_DEBUG_OBJECT (pay, "scanned pu size %u", pu_size);
375
376       /* we need to make a new packet */
377       if (state.force_packet) {
378         GST_DEBUG_OBJECT (pay, "need to force a new packet");
379         state.force_packet = FALSE;
380         pos = end;
381         break;
382       }
383
384       /* else see if we have enough */
385       if (pu_size > max_size) {
386         if (pos != offset)
387           /* the packet became too large, use previous scanpos */
388           pu_size = pos - offset;
389         else
390           /* the already scanned data was already too big, make sure we start
391            * scanning from the last searched position */
392           pos = end;
393
394         GST_DEBUG_OBJECT (pay, "max size exceeded pu_size %u", pu_size);
395         break;
396       }
397
398       pos = end;
399
400       /* exit when finished */
401       if (pos == map.size)
402         break;
403
404       /* scan next packetization unit and fill in the header */
405       end = find_pu_end (pay, map.data, map.size, pos, &state);
406     } while (TRUE);
407
408     while (pu_size > 0) {
409       guint packet_size, data_size;
410       GstBuffer *paybuf;
411
412       /* calculate the packet size */
413       packet_size =
414           gst_rtp_buffer_calc_packet_len (pu_size + HEADER_SIZE, 0, 0);
415
416       if (packet_size > mtu) {
417         GST_DEBUG_OBJECT (pay, "needed packet size %u clamped to MTU %u",
418             packet_size, mtu);
419         packet_size = mtu;
420       } else {
421         GST_DEBUG_OBJECT (pay, "needed packet size %u fits in MTU %u",
422             packet_size, mtu);
423       }
424
425       /* get total payload size and data size */
426       payload_size = gst_rtp_buffer_calc_payload_len (packet_size, 0, 0);
427       data_size = payload_size - HEADER_SIZE;
428
429       /* make buffer for header */
430       outbuf = gst_rtp_buffer_new_allocate (HEADER_SIZE, 0, 0);
431
432       GST_BUFFER_PTS (outbuf) = timestamp;
433
434       gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp);
435
436       /* get pointer to header */
437       header = gst_rtp_buffer_get_payload (&rtp);
438
439       pu_size -= data_size;
440       if (pu_size == 0) {
441         /* reached the end of a packetization unit */
442         if (state.header.MHF) {
443           /* we were doing a header, see if all fit in one packet or if
444            * we had to fragment it */
445           if (offset == 0)
446             state.header.MHF = 3;
447           else
448             state.header.MHF = 2;
449         }
450         if (end >= map.size)
451           gst_rtp_buffer_set_marker (&rtp, TRUE);
452       }
453
454       /*
455        * RtpJ2KHeader:
456        * @tp: type (0 progressive, 1 odd field, 2 even field)
457        * @MHF: Main Header Flag
458        * @mh_id: Main Header Identification
459        * @T: Tile field invalidation flag
460        * @priority: priority
461        * @tile number: the tile number of the payload
462        * @reserved: set to 0
463        * @fragment offset: the byte offset of the current payload
464        *
465        *  0                   1                   2                   3
466        *  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
467        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
468        * |tp |MHF|mh_id|T|     priority  |           tile number         |
469        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
470        * |reserved       |             fragment offset                   |
471        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
472        */
473       header[0] = (state.header.tp << 6) | (state.header.MHF << 4) |
474           (state.header.mh_id << 1) | state.header.T;
475       header[1] = state.header.priority;
476       header[2] = state.header.tile >> 8;
477       header[3] = state.header.tile & 0xff;
478       header[4] = 0;
479       header[5] = state.header.offset >> 16;
480       header[6] = (state.header.offset >> 8) & 0xff;
481       header[7] = state.header.offset & 0xff;
482
483       gst_rtp_buffer_unmap (&rtp);
484
485       /* make subbuffer of j2k data */
486       paybuf = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL,
487           offset, data_size);
488       gst_rtp_copy_meta (GST_ELEMENT_CAST (basepayload), outbuf, paybuf,
489           g_quark_from_static_string (GST_META_TAG_VIDEO_STR));
490       outbuf = gst_buffer_append (outbuf, paybuf);
491
492       gst_buffer_list_add (list, outbuf);
493
494       /* reset header for next round */
495       state.header.MHF = 0;
496       state.header.T = 1;
497       state.header.tile = 0;
498
499       offset += data_size;
500       state.header.offset = offset;
501     }
502     offset = pos;
503   } while (offset < map.size);
504
505   gst_buffer_unref (buffer);
506
507   /* push the whole buffer list at once */
508   ret = gst_rtp_base_payload_push_list (basepayload, list);
509
510   return ret;
511 }
512
513 static void
514 gst_rtp_j2k_pay_set_property (GObject * object, guint prop_id,
515     const GValue * value, GParamSpec * pspec)
516 {
517   switch (prop_id) {
518     default:
519       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
520       break;
521   }
522 }
523
524 static void
525 gst_rtp_j2k_pay_get_property (GObject * object, guint prop_id,
526     GValue * value, GParamSpec * pspec)
527 {
528   switch (prop_id) {
529     default:
530       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
531       break;
532   }
533 }
534
535 gboolean
536 gst_rtp_j2k_pay_plugin_init (GstPlugin * plugin)
537 {
538   return gst_element_register (plugin, "rtpj2kpay", GST_RANK_SECONDARY,
539       GST_TYPE_RTP_J2K_PAY);
540 }