rtpvp8: port some more to new memory API
[platform/upstream/gstreamer.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 enum
82 {
83   PROP_0,
84   PROP_LAST
85 };
86
87 typedef struct
88 {
89   guint tp:2;
90   guint MHF:2;
91   guint mh_id:3;
92   guint T:1;
93   guint priority:8;
94   guint tile:16;
95   guint offset:24;
96 } RtpJ2KHeader;
97
98 #define HEADER_SIZE 8
99
100 static void gst_rtp_j2k_pay_set_property (GObject * object, guint prop_id,
101     const GValue * value, GParamSpec * pspec);
102 static void gst_rtp_j2k_pay_get_property (GObject * object, guint prop_id,
103     GValue * value, GParamSpec * pspec);
104
105 static gboolean gst_rtp_j2k_pay_setcaps (GstRTPBasePayload * basepayload,
106     GstCaps * caps);
107
108 static GstFlowReturn gst_rtp_j2k_pay_handle_buffer (GstRTPBasePayload * pad,
109     GstBuffer * buffer);
110
111 #define gst_rtp_j2k_pay_parent_class parent_class
112 G_DEFINE_TYPE (GstRtpJ2KPay, gst_rtp_j2k_pay, GST_TYPE_RTP_BASE_PAYLOAD);
113
114 static void
115 gst_rtp_j2k_pay_class_init (GstRtpJ2KPayClass * klass)
116 {
117   GObjectClass *gobject_class;
118   GstElementClass *gstelement_class;
119   GstRTPBasePayloadClass *gstrtpbasepayload_class;
120
121   gobject_class = (GObjectClass *) klass;
122   gstelement_class = (GstElementClass *) klass;
123   gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass;
124
125   gobject_class->set_property = gst_rtp_j2k_pay_set_property;
126   gobject_class->get_property = gst_rtp_j2k_pay_get_property;
127
128   gst_element_class_add_pad_template (gstelement_class,
129       gst_static_pad_template_get (&gst_rtp_j2k_pay_src_template));
130   gst_element_class_add_pad_template (gstelement_class,
131       gst_static_pad_template_get (&gst_rtp_j2k_pay_sink_template));
132
133   gst_element_class_set_static_metadata (gstelement_class,
134       "RTP JPEG 2000 payloader", "Codec/Payloader/Network/RTP",
135       "Payload-encodes JPEG 2000 pictures into RTP packets (RFC 5371)",
136       "Wim Taymans <wim.taymans@gmail.com>");
137
138   gstrtpbasepayload_class->set_caps = gst_rtp_j2k_pay_setcaps;
139   gstrtpbasepayload_class->handle_buffer = gst_rtp_j2k_pay_handle_buffer;
140
141   GST_DEBUG_CATEGORY_INIT (rtpj2kpay_debug, "rtpj2kpay", 0,
142       "JPEG 2000 RTP Payloader");
143 }
144
145 static void
146 gst_rtp_j2k_pay_init (GstRtpJ2KPay * pay)
147 {
148 }
149
150 static gboolean
151 gst_rtp_j2k_pay_setcaps (GstRTPBasePayload * basepayload, GstCaps * caps)
152 {
153   GstStructure *caps_structure = gst_caps_get_structure (caps, 0);
154   GstRtpJ2KPay *pay;
155   gint width = 0, height = 0;
156   gboolean res;
157
158   pay = GST_RTP_J2K_PAY (basepayload);
159
160   /* these properties are not mandatory, we can get them from the stream */
161   if (gst_structure_get_int (caps_structure, "height", &height)) {
162     pay->height = height;
163   }
164   if (gst_structure_get_int (caps_structure, "width", &width)) {
165     pay->width = width;
166   }
167
168   gst_rtp_base_payload_set_options (basepayload, "video", TRUE, "JPEG2000",
169       90000);
170   res = gst_rtp_base_payload_set_outcaps (basepayload, NULL);
171
172   return res;
173 }
174
175
176 static guint
177 gst_rtp_j2k_pay_header_size (const guint8 * data, guint offset)
178 {
179   return data[offset] << 8 | data[offset + 1];
180 }
181
182 static RtpJ2KMarker
183 gst_rtp_j2k_pay_scan_marker (const guint8 * data, guint size, guint * offset)
184 {
185   while ((data[(*offset)++] != J2K_MARKER) && ((*offset) < size));
186
187   if (G_UNLIKELY ((*offset) >= size)) {
188     return J2K_MARKER_EOC;
189   } else {
190     guint8 marker = data[(*offset)++];
191     return marker;
192   }
193 }
194
195 typedef struct
196 {
197   RtpJ2KHeader header;
198   gboolean bitstream;
199   guint n_tiles;
200   guint next_sot;
201   gboolean force_packet;
202 } RtpJ2KState;
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   RtpJ2KMarker 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 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 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 == 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 J2K_MARKER_SOC:
245           GST_LOG_OBJECT (pay, "found SOC at %u", offset);
246           state->header.MHF = 1;
247           break;
248         case J2K_MARKER_SOT:
249         {
250           guint len, Psot;
251
252           GST_LOG_OBJECT (pay, "found SOT at %u", offset);
253           /* we found SOT but also had a header first */
254           if (state->header.MHF) {
255             state->force_packet = TRUE;
256             return offset - 2;
257           }
258
259           /* parse SOT but do some sanity checks first */
260           len = gst_rtp_j2k_pay_header_size (data, offset);
261           GST_LOG_OBJECT (pay, "SOT length %u", len);
262           if (len < 8)
263             return size;
264           if (offset + len >= size)
265             return size;
266
267           if (state->n_tiles == 0)
268             /* first tile, T is valid */
269             state->header.T = 0;
270           else
271             /* more tiles, T becomes invalid */
272             state->header.T = 1;
273           state->header.tile = GST_READ_UINT16_BE (&data[offset + 2]);
274           state->n_tiles++;
275
276           /* get offset of next tile, if it's 0, it goes all the way to the end of
277            * the data */
278           Psot = GST_READ_UINT32_BE (&data[offset + 4]);
279           if (Psot == 0)
280             state->next_sot = size;
281           else
282             state->next_sot = offset - 2 + Psot;
283
284           offset += len;
285           GST_LOG_OBJECT (pay, "Isot %u, Psot %u, next %u", state->header.tile,
286               Psot, state->next_sot);
287           break;
288         }
289         case J2K_MARKER_SOD:
290           GST_LOG_OBJECT (pay, "found SOD at %u", offset);
291           /* can't have more tiles now */
292           state->n_tiles = 0;
293           /* go to bitstream parsing */
294           state->bitstream = TRUE;
295           /* cut at the next SOP or else include all data */
296           cut_sop = TRUE;
297           /* force a new packet when we see SOP, this can be optional but the
298            * spec recommends packing headers separately */
299           state->force_packet = TRUE;
300           break;
301         case J2K_MARKER_EOC:
302           GST_LOG_OBJECT (pay, "found EOC at %u", offset);
303           return offset;
304         default:
305         {
306           guint len = gst_rtp_j2k_pay_header_size (data, offset);
307           GST_LOG_OBJECT (pay, "skip 0x%02x len %u", marker, len);
308           offset += len;
309           break;
310         }
311       }
312     }
313   }
314   GST_DEBUG_OBJECT (pay, "reached end of data");
315   return size;
316 }
317
318 static GstFlowReturn
319 gst_rtp_j2k_pay_handle_buffer (GstRTPBasePayload * basepayload,
320     GstBuffer * buffer)
321 {
322   GstRtpJ2KPay *pay;
323   GstClockTime timestamp;
324   GstFlowReturn ret = GST_FLOW_ERROR;
325   RtpJ2KState state;
326   GstBufferList *list = NULL;
327   GstMapInfo map;
328   guint mtu, max_size;
329   guint offset;
330   guint end, pos;
331
332   pay = GST_RTP_J2K_PAY (basepayload);
333   mtu = GST_RTP_BASE_PAYLOAD_MTU (pay);
334
335   gst_buffer_map (buffer, &map, GST_MAP_READ);
336   timestamp = GST_BUFFER_TIMESTAMP (buffer);
337   offset = pos = end = 0;
338
339   GST_LOG_OBJECT (pay,
340       "got buffer size %" G_GSIZE_FORMAT ", timestamp %" GST_TIME_FORMAT,
341       map.size, GST_TIME_ARGS (timestamp));
342
343   /* do some header defaults first */
344   state.header.tp = 0;          /* only progressive scan */
345   state.header.MHF = 0;         /* no header */
346   state.header.mh_id = 0;       /* always 0 for now */
347   state.header.T = 1;           /* invalid tile */
348   state.header.priority = 255;  /* always 255 for now */
349   state.header.tile = 0;        /* no tile number */
350   state.header.offset = 0;      /* offset of 0 */
351   state.bitstream = FALSE;
352   state.n_tiles = 0;
353   state.next_sot = 0;
354   state.force_packet = FALSE;
355
356   list = gst_buffer_list_new ();
357
358   /* get max packet length */
359   max_size = gst_rtp_buffer_calc_payload_len (mtu - HEADER_SIZE, 0, 0);
360
361   do {
362     GstBuffer *outbuf;
363     guint8 *header;
364     guint payload_size;
365     guint pu_size;
366     GstRTPBuffer rtp = { NULL };
367
368     /* try to pack as much as we can */
369     do {
370       /* see how much we have scanned already */
371       pu_size = end - offset;
372       GST_DEBUG_OBJECT (pay, "scanned pu size %u", pu_size);
373
374       /* we need to make a new packet */
375       if (state.force_packet) {
376         GST_DEBUG_OBJECT (pay, "need to force a new packet");
377         state.force_packet = FALSE;
378         pos = end;
379         break;
380       }
381
382       /* else see if we have enough */
383       if (pu_size > max_size) {
384         if (pos != offset)
385           /* the packet became too large, use previous scanpos */
386           pu_size = pos - offset;
387         else
388           /* the already scanned data was already too big, make sure we start
389            * scanning from the last searched position */
390           pos = end;
391
392         GST_DEBUG_OBJECT (pay, "max size exceeded pu_size %u", pu_size);
393         break;
394       }
395
396       pos = end;
397
398       /* exit when finished */
399       if (pos == map.size)
400         break;
401
402       /* scan next packetization unit and fill in the header */
403       end = find_pu_end (pay, map.data, map.size, pos, &state);
404     } while (TRUE);
405
406     while (pu_size > 0) {
407       guint packet_size, data_size;
408       GstBuffer *paybuf;
409
410       /* calculate the packet size */
411       packet_size =
412           gst_rtp_buffer_calc_packet_len (pu_size + HEADER_SIZE, 0, 0);
413
414       if (packet_size > mtu) {
415         GST_DEBUG_OBJECT (pay, "needed packet size %u clamped to MTU %u",
416             packet_size, mtu);
417         packet_size = mtu;
418       } else {
419         GST_DEBUG_OBJECT (pay, "needed packet size %u fits in MTU %u",
420             packet_size, mtu);
421       }
422
423       /* get total payload size and data size */
424       payload_size = gst_rtp_buffer_calc_payload_len (packet_size, 0, 0);
425       data_size = payload_size - HEADER_SIZE;
426
427       /* make buffer for header */
428       outbuf = gst_rtp_buffer_new_allocate (HEADER_SIZE, 0, 0);
429
430       GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
431
432       gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp);
433
434       /* get pointer to header */
435       header = gst_rtp_buffer_get_payload (&rtp);
436
437       pu_size -= data_size;
438       if (pu_size == 0) {
439         /* reached the end of a packetization unit */
440         if (state.header.MHF) {
441           /* we were doing a header, see if all fit in one packet or if
442            * we had to fragment it */
443           if (offset == 0)
444             state.header.MHF = 3;
445           else
446             state.header.MHF = 2;
447         }
448         if (end >= map.size)
449           gst_rtp_buffer_set_marker (&rtp, TRUE);
450       }
451
452       /*
453        * RtpJ2KHeader:
454        * @tp: type (0 progressive, 1 odd field, 2 even field)
455        * @MHF: Main Header Flag
456        * @mh_id: Main Header Identification
457        * @T: Tile field invalidation flag
458        * @priority: priority
459        * @tile number: the tile number of the payload
460        * @reserved: set to 0
461        * @fragment offset: the byte offset of the current payload
462        *
463        *  0                   1                   2                   3
464        *  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
465        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
466        * |tp |MHF|mh_id|T|     priority  |           tile number         |
467        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
468        * |reserved       |             fragment offset                   |
469        * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
470        */
471       header[0] = (state.header.tp << 6) | (state.header.MHF << 4) |
472           (state.header.mh_id << 1) | state.header.T;
473       header[1] = state.header.priority;
474       header[2] = state.header.tile >> 8;
475       header[3] = state.header.tile & 0xff;
476       header[4] = 0;
477       header[5] = state.header.offset >> 16;
478       header[6] = (state.header.offset >> 8) & 0xff;
479       header[7] = state.header.offset & 0xff;
480
481       gst_rtp_buffer_unmap (&rtp);
482
483       /* make subbuffer of j2k data */
484       paybuf = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_MEMORY,
485           offset, data_size);
486
487       outbuf = gst_buffer_append (outbuf, paybuf);
488
489       gst_buffer_list_add (list, outbuf);
490
491       /* reset header for next round */
492       state.header.MHF = 0;
493       state.header.T = 1;
494       state.header.tile = 0;
495
496       offset += data_size;
497     }
498     offset = pos;
499   } while (offset < map.size);
500
501   gst_buffer_unref (buffer);
502
503   /* push the whole buffer list at once */
504   ret = gst_rtp_base_payload_push_list (basepayload, list);
505
506   return ret;
507 }
508
509 static void
510 gst_rtp_j2k_pay_set_property (GObject * object, guint prop_id,
511     const GValue * value, GParamSpec * pspec)
512 {
513   switch (prop_id) {
514     default:
515       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
516       break;
517   }
518 }
519
520 static void
521 gst_rtp_j2k_pay_get_property (GObject * object, guint prop_id,
522     GValue * value, GParamSpec * pspec)
523 {
524   switch (prop_id) {
525     default:
526       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
527       break;
528   }
529 }
530
531 gboolean
532 gst_rtp_j2k_pay_plugin_init (GstPlugin * plugin)
533 {
534   return gst_element_register (plugin, "rtpj2kpay", GST_RANK_SECONDARY,
535       GST_TYPE_RTP_J2K_PAY);
536 }