Merge commit 'a2337b8af45cb5e8c091ff0e1c3ef4b6cc7b20a3' into 0.11
[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., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, 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
38 #include "gstrtpj2kpay.h"
39
40 static GstStaticPadTemplate gst_rtp_j2k_pay_sink_template =
41 GST_STATIC_PAD_TEMPLATE ("sink",
42     GST_PAD_SINK,
43     GST_PAD_ALWAYS,
44     GST_STATIC_CAPS ("image/x-jpc")
45     );
46
47 static GstStaticPadTemplate gst_rtp_j2k_pay_src_template =
48 GST_STATIC_PAD_TEMPLATE ("src",
49     GST_PAD_SRC,
50     GST_PAD_ALWAYS,
51     GST_STATIC_CAPS ("application/x-rtp, "
52         "  media = (string) \"video\", "
53         "  payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
54         "  clock-rate = (int) 90000, "
55         "  encoding-name = (string) \"JPEG2000\"")
56     );
57
58 GST_DEBUG_CATEGORY_STATIC (rtpj2kpay_debug);
59 #define GST_CAT_DEFAULT (rtpj2kpay_debug)
60
61 /*
62  * RtpJ2KMarker:
63  * @J2K_MARKER: Prefix for JPEG 2000 marker
64  * @J2K_MARKER_SOC: Start of Codestream
65  * @J2K_MARKER_SOT: Start of tile
66  * @J2K_MARKER_EOC: End of Codestream
67  *
68  * Identifers for markers in JPEG 2000 codestreams
69  */
70 typedef enum
71 {
72   J2K_MARKER = 0xFF,
73   J2K_MARKER_SOC = 0x4F,
74   J2K_MARKER_SOT = 0x90,
75   J2K_MARKER_SOP = 0x91,
76   J2K_MARKER_EPH = 0x92,
77   J2K_MARKER_SOD = 0x93,
78   J2K_MARKER_EOC = 0xD9
79 } RtpJ2KMarker;
80
81 #define DEFAULT_BUFFER_LIST             TRUE
82
83 enum
84 {
85   PROP_0,
86   PROP_BUFFER_LIST,
87   PROP_LAST
88 };
89
90 typedef struct
91 {
92   guint tp:2;
93   guint MHF:2;
94   guint mh_id:3;
95   guint T:1;
96   guint priority:8;
97   guint tile:16;
98   guint offset:24;
99 } RtpJ2KHeader;
100
101 #define HEADER_SIZE 8
102
103 static void gst_rtp_j2k_pay_set_property (GObject * object, guint prop_id,
104     const GValue * value, GParamSpec * pspec);
105 static void gst_rtp_j2k_pay_get_property (GObject * object, guint prop_id,
106     GValue * value, GParamSpec * pspec);
107
108 static gboolean gst_rtp_j2k_pay_setcaps (GstRTPBasePayload * basepayload,
109     GstCaps * caps);
110
111 static GstFlowReturn gst_rtp_j2k_pay_handle_buffer (GstRTPBasePayload * pad,
112     GstBuffer * buffer);
113
114 #define gst_rtp_j2k_pay_parent_class parent_class
115 G_DEFINE_TYPE (GstRtpJ2KPay, gst_rtp_j2k_pay, GST_TYPE_RTP_BASE_PAYLOAD);
116
117 static void
118 gst_rtp_j2k_pay_class_init (GstRtpJ2KPayClass * klass)
119 {
120   GObjectClass *gobject_class;
121   GstElementClass *gstelement_class;
122   GstRTPBasePayloadClass *gstrtpbasepayload_class;
123
124   gobject_class = (GObjectClass *) klass;
125   gstelement_class = (GstElementClass *) klass;
126   gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass;
127
128   gobject_class->set_property = gst_rtp_j2k_pay_set_property;
129   gobject_class->get_property = gst_rtp_j2k_pay_get_property;
130
131   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BUFFER_LIST,
132       g_param_spec_boolean ("buffer-list", "Buffer List",
133           "Use Buffer Lists",
134           DEFAULT_BUFFER_LIST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
135
136   gst_element_class_add_pad_template (gstelement_class,
137       gst_static_pad_template_get (&gst_rtp_j2k_pay_src_template));
138   gst_element_class_add_pad_template (gstelement_class,
139       gst_static_pad_template_get (&gst_rtp_j2k_pay_sink_template));
140
141   gst_element_class_set_details_simple (gstelement_class,
142       "RTP JPEG 2000 payloader", "Codec/Payloader/Network/RTP",
143       "Payload-encodes JPEG 2000 pictures into RTP packets (RFC 5371)",
144       "Wim Taymans <wim.taymans@gmail.com>");
145
146   gstrtpbasepayload_class->set_caps = gst_rtp_j2k_pay_setcaps;
147   gstrtpbasepayload_class->handle_buffer = gst_rtp_j2k_pay_handle_buffer;
148
149   GST_DEBUG_CATEGORY_INIT (rtpj2kpay_debug, "rtpj2kpay", 0,
150       "JPEG 2000 RTP Payloader");
151 }
152
153 static void
154 gst_rtp_j2k_pay_init (GstRtpJ2KPay * pay)
155 {
156   pay->buffer_list = DEFAULT_BUFFER_LIST;
157 }
158
159 static gboolean
160 gst_rtp_j2k_pay_setcaps (GstRTPBasePayload * basepayload, GstCaps * caps)
161 {
162   GstStructure *caps_structure = gst_caps_get_structure (caps, 0);
163   GstRtpJ2KPay *pay;
164   gint width = 0, height = 0;
165   gboolean res;
166
167   pay = GST_RTP_J2K_PAY (basepayload);
168
169   /* these properties are not mandatory, we can get them from the stream */
170   if (gst_structure_get_int (caps_structure, "height", &height)) {
171     pay->height = height;
172   }
173   if (gst_structure_get_int (caps_structure, "width", &width)) {
174     pay->width = width;
175   }
176
177   gst_rtp_base_payload_set_options (basepayload, "video", TRUE, "JPEG2000",
178       90000);
179   res = gst_rtp_base_payload_set_outcaps (basepayload, NULL);
180
181   return res;
182 }
183
184
185 static guint
186 gst_rtp_j2k_pay_header_size (const guint8 * data, guint offset)
187 {
188   return data[offset] << 8 | data[offset + 1];
189 }
190
191 static RtpJ2KMarker
192 gst_rtp_j2k_pay_scan_marker (const guint8 * data, guint size, guint * offset)
193 {
194   while ((data[(*offset)++] != J2K_MARKER) && ((*offset) < size));
195
196   if (G_UNLIKELY ((*offset) >= size)) {
197     return J2K_MARKER_EOC;
198   } else {
199     guint8 marker = data[(*offset)++];
200     return marker;
201   }
202 }
203
204 typedef struct
205 {
206   RtpJ2KHeader header;
207   gboolean bitstream;
208   guint n_tiles;
209   guint next_sot;
210   gboolean force_packet;
211 } RtpJ2KState;
212
213 static guint
214 find_pu_end (GstRtpJ2KPay * pay, const guint8 * data, guint size,
215     guint offset, RtpJ2KState * state)
216 {
217   gboolean cut_sop = FALSE;
218   RtpJ2KMarker marker;
219
220   /* parse the j2k header for 'start of codestream' */
221   GST_LOG_OBJECT (pay, "checking from offset %u", offset);
222   while (offset < size) {
223     marker = gst_rtp_j2k_pay_scan_marker (data, size, &offset);
224
225     if (state->bitstream) {
226       /* parsing bitstream, only look for SOP */
227       switch (marker) {
228         case J2K_MARKER_SOP:
229           GST_LOG_OBJECT (pay, "found SOP at %u", offset);
230           if (cut_sop)
231             return offset - 2;
232           cut_sop = TRUE;
233           break;
234         case J2K_MARKER_EPH:
235           /* just skip over EPH */
236           GST_LOG_OBJECT (pay, "found EPH at %u", offset);
237           break;
238         default:
239           if (offset >= state->next_sot) {
240             GST_LOG_OBJECT (pay, "reached next SOT at %u", offset);
241             state->bitstream = FALSE;
242             state->force_packet = TRUE;
243             if (marker == J2K_MARKER_EOC && state->next_sot + 2 <= size)
244               /* include EOC but never go past the max size */
245               return state->next_sot + 2;
246             else
247               return state->next_sot;
248           }
249           break;
250       }
251     } else {
252       switch (marker) {
253         case J2K_MARKER_SOC:
254           GST_LOG_OBJECT (pay, "found SOC at %u", offset);
255           state->header.MHF = 1;
256           break;
257         case J2K_MARKER_SOT:
258         {
259           guint len, Psot;
260
261           GST_LOG_OBJECT (pay, "found SOT at %u", offset);
262           /* we found SOT but also had a header first */
263           if (state->header.MHF) {
264             state->force_packet = TRUE;
265             return offset - 2;
266           }
267
268           /* parse SOT but do some sanity checks first */
269           len = gst_rtp_j2k_pay_header_size (data, offset);
270           GST_LOG_OBJECT (pay, "SOT length %u", len);
271           if (len < 8)
272             return size;
273           if (offset + len >= size)
274             return size;
275
276           if (state->n_tiles == 0)
277             /* first tile, T is valid */
278             state->header.T = 0;
279           else
280             /* more tiles, T becomes invalid */
281             state->header.T = 1;
282           state->header.tile = GST_READ_UINT16_BE (&data[offset + 2]);
283           state->n_tiles++;
284
285           /* get offset of next tile, if it's 0, it goes all the way to the end of
286            * the data */
287           Psot = GST_READ_UINT32_BE (&data[offset + 4]);
288           if (Psot == 0)
289             state->next_sot = size;
290           else
291             state->next_sot = offset - 2 + Psot;
292
293           offset += len;
294           GST_LOG_OBJECT (pay, "Isot %u, Psot %u, next %u", state->header.tile,
295               Psot, state->next_sot);
296           break;
297         }
298         case J2K_MARKER_SOD:
299           GST_LOG_OBJECT (pay, "found SOD at %u", offset);
300           /* can't have more tiles now */
301           state->n_tiles = 0;
302           /* go to bitstream parsing */
303           state->bitstream = TRUE;
304           /* cut at the next SOP or else include all data */
305           cut_sop = TRUE;
306           /* force a new packet when we see SOP, this can be optional but the
307            * spec recommends packing headers separately */
308           state->force_packet = TRUE;
309           break;
310         case J2K_MARKER_EOC:
311           GST_LOG_OBJECT (pay, "found EOC at %u", offset);
312           return offset;
313         default:
314         {
315           guint len = gst_rtp_j2k_pay_header_size (data, offset);
316           GST_LOG_OBJECT (pay, "skip 0x%02x len %u", marker, len);
317           offset += len;
318           break;
319         }
320       }
321     }
322   }
323   GST_DEBUG_OBJECT (pay, "reached end of data");
324   return size;
325 }
326
327 static GstFlowReturn
328 gst_rtp_j2k_pay_handle_buffer (GstRTPBasePayload * basepayload,
329     GstBuffer * buffer)
330 {
331   GstRtpJ2KPay *pay;
332   GstClockTime timestamp;
333   GstFlowReturn ret = GST_FLOW_ERROR;
334   RtpJ2KState state;
335 #if 0
336   GstBufferList *list = NULL;
337 #endif
338   guint8 *data;
339   gsize size;
340   guint mtu, max_size;
341   guint offset;
342   guint end, pos;
343
344   pay = GST_RTP_J2K_PAY (basepayload);
345   mtu = GST_RTP_BASE_PAYLOAD_MTU (pay);
346
347   data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
348   timestamp = GST_BUFFER_TIMESTAMP (buffer);
349   offset = pos = end = 0;
350
351   GST_LOG_OBJECT (pay,
352       "got buffer size %" G_GSIZE_FORMAT ", timestamp %" GST_TIME_FORMAT, size,
353       GST_TIME_ARGS (timestamp));
354
355   /* do some header defaults first */
356   state.header.tp = 0;          /* only progressive scan */
357   state.header.MHF = 0;         /* no header */
358   state.header.mh_id = 0;       /* always 0 for now */
359   state.header.T = 1;           /* invalid tile */
360   state.header.priority = 255;  /* always 255 for now */
361   state.header.tile = 0;        /* no tile number */
362   state.header.offset = 0;      /* offset of 0 */
363   state.bitstream = FALSE;
364   state.n_tiles = 0;
365   state.next_sot = 0;
366   state.force_packet = FALSE;
367
368 #if 0
369   if (pay->buffer_list) {
370     list = gst_buffer_list_new ();
371     it = gst_buffer_list_iterate (list);
372   }
373 #endif
374
375   /* get max packet length */
376   max_size = gst_rtp_buffer_calc_payload_len (mtu - HEADER_SIZE, 0, 0);
377
378   do {
379     GstBuffer *outbuf;
380     guint8 *header;
381     guint payload_size;
382     guint pu_size;
383     GstRTPBuffer rtp = { NULL };
384
385     /* try to pack as much as we can */
386     do {
387       /* see how much we have scanned already */
388       pu_size = end - offset;
389       GST_DEBUG_OBJECT (pay, "scanned pu size %u", pu_size);
390
391       /* we need to make a new packet */
392       if (state.force_packet) {
393         GST_DEBUG_OBJECT (pay, "need to force a new packet");
394         state.force_packet = FALSE;
395         pos = end;
396         break;
397       }
398
399       /* else see if we have enough */
400       if (pu_size > max_size) {
401         if (pos != offset)
402           /* the packet became too large, use previous scanpos */
403           pu_size = pos - offset;
404         else
405           /* the already scanned data was already too big, make sure we start
406            * scanning from the last searched position */
407           pos = end;
408
409         GST_DEBUG_OBJECT (pay, "max size exceeded pu_size %u", pu_size);
410         break;
411       }
412
413       pos = end;
414
415       /* exit when finished */
416       if (pos == size)
417         break;
418
419       /* scan next packetization unit and fill in the header */
420       end = find_pu_end (pay, data, size, pos, &state);
421     } while (TRUE);
422
423     while (pu_size > 0) {
424       guint packet_size, data_size;
425
426       /* calculate the packet size */
427       packet_size =
428           gst_rtp_buffer_calc_packet_len (pu_size + HEADER_SIZE, 0, 0);
429
430       if (packet_size > mtu) {
431         GST_DEBUG_OBJECT (pay, "needed packet size %u clamped to MTU %u",
432             packet_size, mtu);
433         packet_size = mtu;
434       } else {
435         GST_DEBUG_OBJECT (pay, "needed packet size %u fits in MTU %u",
436             packet_size, mtu);
437       }
438
439       /* get total payload size and data size */
440       payload_size = gst_rtp_buffer_calc_payload_len (packet_size, 0, 0);
441       data_size = payload_size - HEADER_SIZE;
442
443 #if 0
444       if (pay->buffer_list) {
445         /* make buffer for header */
446         outbuf = gst_rtp_buffer_new_allocate (HEADER_SIZE, 0, 0);
447       } else
448 #endif
449       {
450         /* make buffer for header and data */
451         outbuf = gst_rtp_buffer_new_allocate (payload_size, 0, 0);
452       }
453       GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
454
455       gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp);
456
457       /* get pointer to header */
458       header = gst_rtp_buffer_get_payload (&rtp);
459
460       pu_size -= data_size;
461       if (pu_size == 0) {
462         /* reached the end of a packetization unit */
463         if (state.header.MHF) {
464           /* we were doing a header, see if all fit in one packet or if
465            * we had to fragment it */
466           if (offset == 0)
467             state.header.MHF = 3;
468           else
469             state.header.MHF = 2;
470         }
471         if (end >= size)
472           gst_rtp_buffer_set_marker (&rtp, TRUE);
473       }
474
475       /*
476        * RtpJ2KHeader:
477        * @tp: type (0 progressive, 1 odd field, 2 even field)
478        * @MHF: Main Header Flag
479        * @mh_id: Main Header Identification
480        * @T: Tile field invalidation flag
481        * @priority: priority
482        * @tile number: the tile number of the payload
483        * @reserved: set to 0
484        * @fragment offset: the byte offset of the current payload
485        *
486        *  0                   1                   2                   3
487        *  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
488        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
489        * |tp |MHF|mh_id|T|     priority  |           tile number         |
490        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
491        * |reserved       |             fragment offset                   |
492        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
493        */
494       header[0] = (state.header.tp << 6) | (state.header.MHF << 4) |
495           (state.header.mh_id << 1) | state.header.T;
496       header[1] = state.header.priority;
497       header[2] = state.header.tile >> 8;
498       header[3] = state.header.tile & 0xff;
499       header[4] = 0;
500       header[5] = state.header.offset >> 16;
501       header[6] = (state.header.offset >> 8) & 0xff;
502       header[7] = state.header.offset & 0xff;
503
504 #if 0
505       if (pay->buffer_list) {
506         GstBuffer *paybuf;
507
508         /* make subbuffer of j2k data */
509         paybuf = gst_buffer_create_sub (buffer, offset, data_size);
510
511         /* create a new group to hold the header and the payload */
512         gst_buffer_list_iterator_add_group (it);
513
514         /* add both buffers to the buffer list */
515         gst_buffer_list_iterator_add (it, outbuf);
516         gst_buffer_list_iterator_add (it, paybuf);
517       } else
518 #endif
519       {
520         /* copy payload */
521         memcpy (header + HEADER_SIZE, &data[offset], data_size);
522         gst_rtp_buffer_unmap (&rtp);
523
524         ret = gst_rtp_base_payload_push (basepayload, outbuf);
525         if (ret != GST_FLOW_OK)
526           goto done;
527       }
528
529       /* reset header for next round */
530       state.header.MHF = 0;
531       state.header.T = 1;
532       state.header.tile = 0;
533
534       offset += data_size;
535     }
536     offset = pos;
537   } while (offset < size);
538
539 done:
540   gst_buffer_unref (buffer);
541
542 #if 0
543   if (pay->buffer_list) {
544     /* free iterator and push the whole buffer list at once */
545     gst_buffer_list_iterator_free (it);
546     ret = gst_rtp_base_payload_push_list (basepayload, list);
547   }
548 #endif
549
550   return ret;
551 }
552
553 static void
554 gst_rtp_j2k_pay_set_property (GObject * object, guint prop_id,
555     const GValue * value, GParamSpec * pspec)
556 {
557   GstRtpJ2KPay *rtpj2kpay;
558
559   rtpj2kpay = GST_RTP_J2K_PAY (object);
560
561   switch (prop_id) {
562     case PROP_BUFFER_LIST:
563       rtpj2kpay->buffer_list = g_value_get_boolean (value);
564       break;
565     default:
566       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
567       break;
568   }
569 }
570
571 static void
572 gst_rtp_j2k_pay_get_property (GObject * object, guint prop_id,
573     GValue * value, GParamSpec * pspec)
574 {
575   GstRtpJ2KPay *rtpj2kpay;
576
577   rtpj2kpay = GST_RTP_J2K_PAY (object);
578
579   switch (prop_id) {
580     case PROP_BUFFER_LIST:
581       g_value_set_boolean (value, rtpj2kpay->buffer_list);
582       break;
583     default:
584       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
585       break;
586   }
587 }
588
589 gboolean
590 gst_rtp_j2k_pay_plugin_init (GstPlugin * plugin)
591 {
592   return gst_element_register (plugin, "rtpj2kpay", GST_RANK_SECONDARY,
593       GST_TYPE_RTP_J2K_PAY);
594 }