rtpj2kpay: fix compiler warning on OS/X
[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 images into RTP packets according to RFC 5371
24  * and RFC 5372.
25  * For detailed information see: https://datatracker.ietf.org/doc/rfc5371/
26  * and https://datatracker.ietf.org/doc/rfc5372/
27  *
28  * The payloader takes a JPEG 2000 image, scans it for "packetization
29  * units" and constructs the RTP packet header followed by the JPEG 2000
30  * codestream. A "packetization unit" is defined as either a JPEG 2000 main header,
31  * a JPEG 2000 tile-part header, or a JPEG 2000 packet.
32  *
33  *
34  */
35
36 #ifdef HAVE_CONFIG_H
37 #  include "config.h"
38 #endif
39
40 #include <string.h>
41 #include <gst/rtp/gstrtpbuffer.h>
42 #include <gst/video/video.h>
43 #include "gstrtpj2kcommon.h"
44 #include "gstrtpj2kpay.h"
45 #include "gstrtputils.h"
46
47 static GstStaticPadTemplate gst_rtp_j2k_pay_sink_template =
48 GST_STATIC_PAD_TEMPLATE ("sink",
49     GST_PAD_SINK,
50     GST_PAD_ALWAYS,
51     GST_STATIC_CAPS ("image/x-jpc, " GST_RTP_J2K_SAMPLING_LIST)
52     );
53
54
55 static GstStaticPadTemplate gst_rtp_j2k_pay_src_template =
56 GST_STATIC_PAD_TEMPLATE ("src",
57     GST_PAD_SRC,
58     GST_PAD_ALWAYS,
59     GST_STATIC_CAPS ("application/x-rtp, "
60         "  media = (string) \"video\", "
61         "  payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
62         "  clock-rate = (int) 90000, "
63         GST_RTP_J2K_SAMPLING_LIST "," "  encoding-name = (string) \"JPEG2000\"")
64     );
65
66 GST_DEBUG_CATEGORY_STATIC (rtpj2kpay_debug);
67 #define GST_CAT_DEFAULT (rtpj2kpay_debug)
68
69
70 enum
71 {
72   PROP_0,
73   PROP_LAST
74 };
75
76 typedef struct
77 {
78   guint tp:2;
79   guint MHF:2;
80   guint mh_id:3;
81   guint T:1;
82   guint priority:8;
83   guint tile:16;
84   guint offset:24;
85 } RtpJ2KHeader;
86
87 static void gst_rtp_j2k_pay_set_property (GObject * object, guint prop_id,
88     const GValue * value, GParamSpec * pspec);
89 static void gst_rtp_j2k_pay_get_property (GObject * object, guint prop_id,
90     GValue * value, GParamSpec * pspec);
91
92 static gboolean gst_rtp_j2k_pay_setcaps (GstRTPBasePayload * basepayload,
93     GstCaps * caps);
94
95 static GstFlowReturn gst_rtp_j2k_pay_handle_buffer (GstRTPBasePayload * pad,
96     GstBuffer * buffer);
97
98 #define gst_rtp_j2k_pay_parent_class parent_class
99 G_DEFINE_TYPE (GstRtpJ2KPay, gst_rtp_j2k_pay, GST_TYPE_RTP_BASE_PAYLOAD);
100
101 static void
102 gst_rtp_j2k_pay_class_init (GstRtpJ2KPayClass * klass)
103 {
104   GObjectClass *gobject_class;
105   GstElementClass *gstelement_class;
106   GstRTPBasePayloadClass *gstrtpbasepayload_class;
107
108   gobject_class = (GObjectClass *) klass;
109   gstelement_class = (GstElementClass *) klass;
110   gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass;
111
112   gobject_class->set_property = gst_rtp_j2k_pay_set_property;
113   gobject_class->get_property = gst_rtp_j2k_pay_get_property;
114
115   gst_element_class_add_static_pad_template (gstelement_class,
116       &gst_rtp_j2k_pay_src_template);
117   gst_element_class_add_static_pad_template (gstelement_class,
118       &gst_rtp_j2k_pay_sink_template);
119
120   gst_element_class_set_static_metadata (gstelement_class,
121       "RTP JPEG 2000 payloader", "Codec/Payloader/Network/RTP",
122       "Payload-encodes JPEG 2000 pictures into RTP packets (RFC 5371)",
123       "Wim Taymans <wim.taymans@gmail.com>");
124
125   gstrtpbasepayload_class->set_caps = gst_rtp_j2k_pay_setcaps;
126   gstrtpbasepayload_class->handle_buffer = gst_rtp_j2k_pay_handle_buffer;
127
128   GST_DEBUG_CATEGORY_INIT (rtpj2kpay_debug, "rtpj2kpay", 0,
129       "JPEG 2000 RTP Payloader");
130 }
131
132 static void
133 gst_rtp_j2k_pay_init (GstRtpJ2KPay * pay)
134 {
135 }
136
137 static gboolean
138 gst_rtp_j2k_pay_setcaps (GstRTPBasePayload * basepayload, GstCaps * caps)
139 {
140   GstStructure *caps_structure = gst_caps_get_structure (caps, 0);
141   gboolean res;
142   gint width = 0, height = 0;
143   const gchar *sampling = NULL;
144
145   gboolean has_width = gst_structure_get_int (caps_structure, "width", &width);
146   gboolean has_height =
147       gst_structure_get_int (caps_structure, "height", &height);
148
149
150   /* sampling is a required field */
151   sampling = gst_structure_get_string (caps_structure, "sampling");
152
153   gst_rtp_base_payload_set_options (basepayload, "video", TRUE, "JPEG2000",
154       90000);
155
156   if (has_width && has_height)
157     res = gst_rtp_base_payload_set_outcaps (basepayload,
158         "sampling", G_TYPE_STRING, sampling, "width", G_TYPE_INT, width,
159         "height", G_TYPE_INT, height, NULL);
160   else
161     res =
162         gst_rtp_base_payload_set_outcaps (basepayload, "sampling",
163         G_TYPE_STRING, sampling, NULL);
164   return res;
165 }
166
167
168 static guint
169 gst_rtp_j2k_pay_header_size (const guint8 * data, guint offset)
170 {
171   return data[offset] << 8 | data[offset + 1];
172 }
173
174
175 static GstRtpJ2KMarker
176 gst_rtp_j2k_pay_scan_marker (const guint8 * data, guint size, guint * offset)
177 {
178   while ((data[(*offset)++] != GST_J2K_MARKER) && ((*offset) < size));
179
180   if (G_UNLIKELY ((*offset) >= size)) {
181     return GST_J2K_MARKER_EOC;
182   } else {
183     guint8 marker = data[(*offset)++];
184     return (GstRtpJ2KMarker) marker;
185   }
186 }
187
188 typedef struct
189 {
190   RtpJ2KHeader header;
191   gboolean multi_tile_part;
192   gboolean bitstream;
193   guint next_sot;
194   gboolean force_packet;
195 } RtpJ2KState;
196
197
198 /* Note: The standard recommends that headers be put in their own RTP packets, so we follow
199  * this recommendation in the code. Also, this method groups together all J2K packets
200  * for a tile part and treats this group as a packetization unit. According to the RFC,
201  * only an individual J2K packet is considered a packetization unit.
202  */
203
204 static guint
205 find_pu_end (GstRtpJ2KPay * pay, const guint8 * data, guint size,
206     guint offset, RtpJ2KState * state)
207 {
208   gboolean cut_sop = FALSE;
209   GstRtpJ2KMarker marker;
210
211   /* parse the j2k header for 'start of codestream' */
212   GST_LOG_OBJECT (pay, "checking from offset %u", offset);
213   while (offset < size) {
214     marker = gst_rtp_j2k_pay_scan_marker (data, size, &offset);
215
216     if (state->bitstream) {
217       /* parsing bitstream, only look for SOP */
218       switch (marker) {
219         case GST_J2K_MARKER_SOP:
220           GST_LOG_OBJECT (pay, "found SOP at %u", offset);
221           if (cut_sop)
222             return offset - 2;
223           cut_sop = TRUE;
224           break;
225         case GST_J2K_MARKER_EPH:
226           /* just skip over EPH */
227           GST_LOG_OBJECT (pay, "found EPH at %u", offset);
228           break;
229         default:
230           if (offset >= state->next_sot) {
231             GST_LOG_OBJECT (pay, "reached next SOT at %u", offset);
232             state->bitstream = FALSE;
233             state->force_packet = TRUE;
234             if (marker == GST_J2K_MARKER_EOC && state->next_sot + 2 <= size)
235               /* include EOC but never go past the max size */
236               return state->next_sot + 2;
237             else
238               return state->next_sot;
239           }
240           break;
241       }
242     } else {
243       switch (marker) {
244         case GST_J2K_MARKER_SOC:
245           GST_LOG_OBJECT (pay, "found SOC at %u", offset);
246           /* start off by assuming that we will fit the entire header
247              into the RTP payload */
248           state->header.MHF = 3;
249           break;
250         case GST_J2K_MARKER_SOT:
251         {
252           guint len, Psot, tile;
253
254           GST_LOG_OBJECT (pay, "found SOT at %u", offset);
255           /* SOT for first tile part in code stream:
256              force close of current RTP packet, so that it
257              only contains main header  */
258           if (state->header.MHF) {
259             state->force_packet = TRUE;
260             return offset - 2;
261           }
262
263           /* parse SOT but do some sanity checks first */
264           len = gst_rtp_j2k_pay_header_size (data, offset);
265           GST_LOG_OBJECT (pay, "SOT length %u", len);
266           if (len < 8)
267             return size;
268           if (offset + len >= size)
269             return size;
270
271           /* Isot */
272           tile = GST_READ_UINT16_BE (&data[offset + 2]);
273
274           if (!state->multi_tile_part) {
275
276             /* tile is marked as valid */
277             state->header.T = 0;
278
279             /* we have detected multiple tile parts in this rtp packet : tile bit is now invalid */
280             if (state->header.tile != tile) {
281               state->header.T = 1;
282               state->multi_tile_part = TRUE;
283             }
284           }
285           state->header.tile = tile;
286
287           /* Note: Tile parts from multiple tiles in single RTP packet 
288              will make T invalid.
289              This cannot happen in our case since we always
290              send tile headers in their own RTP packets, so we cannot mix
291              tile parts in a single RTP packet  */
292
293           /* Psot: offset of next tile. If it's 0, next tile goes all the way
294              to the end of the data */
295           Psot = GST_READ_UINT32_BE (&data[offset + 4]);
296           if (Psot == 0)
297             state->next_sot = size;
298           else
299             state->next_sot = offset - 2 + Psot;
300
301           offset += len;
302           GST_LOG_OBJECT (pay, "Isot %u, Psot %u, next %u", state->header.tile,
303               Psot, state->next_sot);
304           break;
305         }
306         case GST_J2K_MARKER_SOD:
307           GST_LOG_OBJECT (pay, "found SOD at %u", offset);
308           /* go to bitstream parsing */
309           state->bitstream = TRUE;
310           /* cut at the next SOP or else include all data */
311           cut_sop = TRUE;
312           /* force a new packet when we see SOP, this can be optional but the
313            * spec recommends packing headers separately */
314           state->force_packet = TRUE;
315           break;
316         case GST_J2K_MARKER_EOC:
317           GST_LOG_OBJECT (pay, "found EOC at %u", offset);
318           return offset;
319         default:
320         {
321           guint len = gst_rtp_j2k_pay_header_size (data, offset);
322           GST_LOG_OBJECT (pay, "skip 0x%02x len %u", marker, len);
323           offset += len;
324           break;
325         }
326       }
327     }
328   }
329   GST_DEBUG_OBJECT (pay, "reached end of data");
330   return size;
331 }
332
333 static GstFlowReturn
334 gst_rtp_j2k_pay_handle_buffer (GstRTPBasePayload * basepayload,
335     GstBuffer * buffer)
336 {
337   GstRtpJ2KPay *pay;
338   GstClockTime timestamp;
339   GstFlowReturn ret = GST_FLOW_ERROR;
340   RtpJ2KState state;
341   GstBufferList *list = NULL;
342   GstMapInfo map;
343   guint mtu, max_size;
344   guint offset;
345   guint end, pos;
346
347   pay = GST_RTP_J2K_PAY (basepayload);
348   mtu = GST_RTP_BASE_PAYLOAD_MTU (pay);
349
350   gst_buffer_map (buffer, &map, GST_MAP_READ);
351   timestamp = GST_BUFFER_PTS (buffer);
352   offset = pos = end = 0;
353
354   GST_LOG_OBJECT (pay,
355       "got buffer size %" G_GSIZE_FORMAT ", timestamp %" GST_TIME_FORMAT,
356       map.size, GST_TIME_ARGS (timestamp));
357
358   /* do some header defaults first */
359   state.header.tp = 0;          /* only progressive scan */
360   state.header.MHF = 0;         /* no header */
361   state.header.mh_id = 0;       /* always 0 for now */
362   state.header.T = 1;           /* invalid tile, because we always begin with the main header */
363   state.header.priority = 255;  /* always 255 for now */
364   state.header.tile = 0xffff;   /* no tile number */
365   state.header.offset = 0;      /* offset of 0 */
366   state.multi_tile_part = FALSE;
367   state.bitstream = FALSE;
368   state.next_sot = 0;
369   state.force_packet = FALSE;
370
371   /* get max packet length */
372   max_size =
373       gst_rtp_buffer_calc_payload_len (mtu - GST_RTP_J2K_HEADER_SIZE, 0, 0);
374
375   list = gst_buffer_list_new_sized ((mtu / max_size) + 1);
376
377   do {
378     GstBuffer *outbuf;
379     guint8 *header;
380     guint payload_size;
381     guint pu_size;
382     GstRTPBuffer rtp = { NULL };
383
384     /* try to pack as much as we can */
385     do {
386       /* see how much we have scanned already */
387       pu_size = end - offset;
388       GST_DEBUG_OBJECT (pay, "scanned pu size %u", pu_size);
389
390       /* we need to make a new packet */
391       if (state.force_packet) {
392         GST_DEBUG_OBJECT (pay, "need to force a new packet");
393         state.force_packet = FALSE;
394         pos = end;
395         break;
396       }
397
398       /* else see if we have enough */
399       if (pu_size > max_size) {
400         if (pos != offset)
401           /* the packet became too large, use previous scanpos */
402           pu_size = pos - offset;
403         else
404           /* the already scanned data was already too big, make sure we start
405            * scanning from the last searched position */
406           pos = end;
407
408         GST_DEBUG_OBJECT (pay, "max size exceeded pu_size %u", pu_size);
409         break;
410       }
411
412       pos = end;
413
414       /* exit when finished */
415       if (pos == map.size)
416         break;
417
418       /* scan next packetization unit and fill in the header */
419       end = find_pu_end (pay, map.data, map.size, pos, &state);
420     } while (TRUE);
421
422     while (pu_size > 0) {
423       guint packet_size, data_size;
424       GstBuffer *paybuf;
425
426       /* calculate the packet size */
427       packet_size =
428           gst_rtp_buffer_calc_packet_len (pu_size + GST_RTP_J2K_HEADER_SIZE, 0,
429           0);
430
431       if (packet_size > mtu) {
432         GST_DEBUG_OBJECT (pay, "needed packet size %u clamped to MTU %u",
433             packet_size, mtu);
434         packet_size = mtu;
435       } else {
436         GST_DEBUG_OBJECT (pay, "needed packet size %u fits in MTU %u",
437             packet_size, mtu);
438       }
439
440       /* get total payload size and data size */
441       payload_size = gst_rtp_buffer_calc_payload_len (packet_size, 0, 0);
442       data_size = payload_size - GST_RTP_J2K_HEADER_SIZE;
443
444       /* make buffer for header */
445       outbuf = gst_rtp_buffer_new_allocate (GST_RTP_J2K_HEADER_SIZE, 0, 0);
446
447       GST_BUFFER_PTS (outbuf) = timestamp;
448
449       gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp);
450
451       /* get pointer to header */
452       header = gst_rtp_buffer_get_payload (&rtp);
453
454       pu_size -= data_size;
455
456       /* reached the end of a packetization unit */
457       if (pu_size == 0 && end >= map.size) {
458         gst_rtp_buffer_set_marker (&rtp, TRUE);
459       }
460       /* If we were processing a header, see if all fits in one RTP packet
461          or if we have to fragment it */
462       if (state.header.MHF) {
463         switch (state.header.MHF) {
464           case 3:
465             if (pu_size > 0)
466               state.header.MHF = 1;
467             break;
468           case 1:
469             if (pu_size == 0)
470               state.header.MHF = 2;
471             break;
472           default:
473             break;
474         }
475       }
476
477       /*
478        * RtpJ2KHeader:
479        * @tp: type (0 progressive, 1 odd field, 2 even field)
480        * @MHF: Main Header Flag
481        * @mh_id: Main Header Identification
482        * @T: Tile field invalidation flag
483        * @priority: priority
484        * @tile number: the tile number of the payload
485        * @reserved: set to 0
486        * @fragment offset: the byte offset of the current payload
487        *
488        *  0                   1                   2                   3
489        *  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
490        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
491        * |tp |MHF|mh_id|T|     priority  |           tile number         |
492        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
493        * |reserved       |             fragment offset                   |
494        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
495        */
496       header[0] = (state.header.tp << 6) | (state.header.MHF << 4) |
497           (state.header.mh_id << 1) | state.header.T;
498       header[1] = state.header.priority;
499       header[2] = state.header.tile >> 8;
500       header[3] = state.header.tile & 0xff;
501       header[4] = 0;
502       header[5] = state.header.offset >> 16;
503       header[6] = (state.header.offset >> 8) & 0xff;
504       header[7] = state.header.offset & 0xff;
505
506       gst_rtp_buffer_unmap (&rtp);
507
508       /* make subbuffer of j2k data */
509       paybuf = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL,
510           offset, data_size);
511       gst_rtp_copy_meta (GST_ELEMENT_CAST (basepayload), outbuf, paybuf,
512           g_quark_from_static_string (GST_META_TAG_VIDEO_STR));
513       outbuf = gst_buffer_append (outbuf, paybuf);
514
515       gst_buffer_list_add (list, outbuf);
516
517       /* reset multi_tile */
518       state.multi_tile_part = FALSE;
519
520
521       /* set MHF to zero if there is no more main header to process */
522       if (state.header.MHF & 2)
523         state.header.MHF = 0;
524
525       /* tile is valid, if there is no more header to process */
526       if (!state.header.MHF)
527         state.header.T = 0;
528
529
530       offset += data_size;
531       state.header.offset = offset;
532     }
533     offset = pos;
534   } while (offset < map.size);
535
536   gst_buffer_unmap (buffer, &map);
537   gst_buffer_unref (buffer);
538
539   /* push the whole buffer list at once */
540   ret = gst_rtp_base_payload_push_list (basepayload, list);
541
542   return ret;
543 }
544
545 static void
546 gst_rtp_j2k_pay_set_property (GObject * object, guint prop_id,
547     const GValue * value, GParamSpec * pspec)
548 {
549   switch (prop_id) {
550     default:
551       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
552       break;
553   }
554 }
555
556 static void
557 gst_rtp_j2k_pay_get_property (GObject * object, guint prop_id,
558     GValue * value, GParamSpec * pspec)
559 {
560   switch (prop_id) {
561     default:
562       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
563       break;
564   }
565 }
566
567 gboolean
568 gst_rtp_j2k_pay_plugin_init (GstPlugin * plugin)
569 {
570   return gst_element_register (plugin, "rtpj2kpay", GST_RANK_SECONDARY,
571       GST_TYPE_RTP_J2K_PAY);
572 }