1 /* GStreamer
2 * Copyright (C) <2006> 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 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 #include <string.h>
25
26 #include <gst/rtp/gstrtpbuffer.h>
27 #include <gst/video/video.h>
28
29 #include "gstrtpelements.h"
30 #include "fnv1hash.h"
31 #include "gstrtptheorapay.h"
32 #include "gstrtputils.h"
33
34 #define THEORA_ID_LEN 42
35
36 GST_DEBUG_CATEGORY_STATIC (rtptheorapay_debug);
37 #define GST_CAT_DEFAULT (rtptheorapay_debug)
38
39 /* references:
40 * http://svn.xiph.org/trunk/theora/doc/draft-ietf-avt-rtp-theora-01.txt
41 */
42
43 static GstStaticPadTemplate gst_rtp_theora_pay_src_template =
44 GST_STATIC_PAD_TEMPLATE ("src",
45 GST_PAD_SRC,
46 GST_PAD_ALWAYS,
47 GST_STATIC_CAPS ("application/x-rtp, "
48 "media = (string) \"video\", "
49 "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
50 "clock-rate = (int) 90000, " "encoding-name = (string) \"THEORA\""
51 /* All required parameters
52 *
53 * "sampling = (string) { "YCbCr-4:2:0", "YCbCr-4:2:2", "YCbCr-4:4:4" } "
54 * "width = (string) [1, 1048561] (multiples of 16) "
55 * "height = (string) [1, 1048561] (multiples of 16) "
56 * "configuration = (string) ANY"
57 */
58 /* All optional parameters
59 *
60 * "configuration-uri ="
61 * "delivery-method = (string) { inline, in_band, out_band/<specific_name> } "
62 */
63 )
64 );
65
66 static GstStaticPadTemplate gst_rtp_theora_pay_sink_template =
67 GST_STATIC_PAD_TEMPLATE ("sink",
68 GST_PAD_SINK,
69 GST_PAD_ALWAYS,
70 GST_STATIC_CAPS ("video/x-theora")
71 );
72
73 #define DEFAULT_CONFIG_INTERVAL 0
74
75 enum
76 {
77 PROP_0,
78 PROP_CONFIG_INTERVAL
79 };
80
81 #define gst_rtp_theora_pay_parent_class parent_class
82 G_DEFINE_TYPE (GstRtpTheoraPay, gst_rtp_theora_pay, GST_TYPE_RTP_BASE_PAYLOAD);
83 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (rtptheorapay, "rtptheorapay",
84 GST_RANK_SECONDARY, GST_TYPE_RTP_THEORA_PAY, rtp_element_init (plugin));
85
86 static gboolean gst_rtp_theora_pay_setcaps (GstRTPBasePayload * basepayload,
87 GstCaps * caps);
88 static GstStateChangeReturn gst_rtp_theora_pay_change_state (GstElement *
89 element, GstStateChange transition);
90 static GstFlowReturn gst_rtp_theora_pay_handle_buffer (GstRTPBasePayload * pad,
91 GstBuffer * buffer);
92 static gboolean gst_rtp_theora_pay_sink_event (GstRTPBasePayload * payload,
93 GstEvent * event);
94
95 static gboolean gst_rtp_theora_pay_parse_id (GstRTPBasePayload * basepayload,
96 guint8 * data, guint size);
97 static gboolean gst_rtp_theora_pay_finish_headers (GstRTPBasePayload *
98 basepayload);
99
100 static void gst_rtp_theora_pay_set_property (GObject * object, guint prop_id,
101 const GValue * value, GParamSpec * pspec);
102 static void gst_rtp_theora_pay_get_property (GObject * object, guint prop_id,
103 GValue * value, GParamSpec * pspec);
104
105 static void
gst_rtp_theora_pay_class_init(GstRtpTheoraPayClass * klass)106 gst_rtp_theora_pay_class_init (GstRtpTheoraPayClass * klass)
107 {
108 GObjectClass *gobject_class;
109 GstElementClass *gstelement_class;
110 GstRTPBasePayloadClass *gstrtpbasepayload_class;
111
112 gobject_class = (GObjectClass *) klass;
113 gstelement_class = (GstElementClass *) klass;
114 gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass;
115
116 gstelement_class->change_state = gst_rtp_theora_pay_change_state;
117
118 gstrtpbasepayload_class->set_caps = gst_rtp_theora_pay_setcaps;
119 gstrtpbasepayload_class->handle_buffer = gst_rtp_theora_pay_handle_buffer;
120 gstrtpbasepayload_class->sink_event = gst_rtp_theora_pay_sink_event;
121
122 gobject_class->set_property = gst_rtp_theora_pay_set_property;
123 gobject_class->get_property = gst_rtp_theora_pay_get_property;
124
125 gst_element_class_add_static_pad_template (gstelement_class,
126 &gst_rtp_theora_pay_src_template);
127 gst_element_class_add_static_pad_template (gstelement_class,
128 &gst_rtp_theora_pay_sink_template);
129
130 gst_element_class_set_static_metadata (gstelement_class,
131 "RTP Theora payloader", "Codec/Payloader/Network/RTP",
132 "Payload-encode Theora video into RTP packets (draft-01 RFC XXXX)",
133 "Wim Taymans <wim.taymans@gmail.com>");
134
135 GST_DEBUG_CATEGORY_INIT (rtptheorapay_debug, "rtptheorapay", 0,
136 "Theora RTP Payloader");
137
138 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_CONFIG_INTERVAL,
139 g_param_spec_uint ("config-interval", "Config Send Interval",
140 "Send Config Insertion Interval in seconds (configuration headers "
141 "will be multiplexed in the data stream when detected.) (0 = disabled)",
142 0, 3600, DEFAULT_CONFIG_INTERVAL,
143 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
144 );
145 }
146
147 static void
gst_rtp_theora_pay_init(GstRtpTheoraPay * rtptheorapay)148 gst_rtp_theora_pay_init (GstRtpTheoraPay * rtptheorapay)
149 {
150 rtptheorapay->last_config = GST_CLOCK_TIME_NONE;
151 }
152
153 static void
gst_rtp_theora_pay_clear_packet(GstRtpTheoraPay * rtptheorapay)154 gst_rtp_theora_pay_clear_packet (GstRtpTheoraPay * rtptheorapay)
155 {
156 if (rtptheorapay->packet)
157 gst_buffer_unref (rtptheorapay->packet);
158 rtptheorapay->packet = NULL;
159 g_list_free_full (rtptheorapay->packet_buffers,
160 (GDestroyNotify) gst_buffer_unref);
161 rtptheorapay->packet_buffers = NULL;
162 }
163
164 static void
gst_rtp_theora_pay_cleanup(GstRtpTheoraPay * rtptheorapay)165 gst_rtp_theora_pay_cleanup (GstRtpTheoraPay * rtptheorapay)
166 {
167 gst_rtp_theora_pay_clear_packet (rtptheorapay);
168 g_list_free_full (rtptheorapay->headers, (GDestroyNotify) gst_buffer_unref);
169 rtptheorapay->headers = NULL;
170 g_free (rtptheorapay->config_data);
171 rtptheorapay->config_data = NULL;
172 rtptheorapay->last_config = GST_CLOCK_TIME_NONE;
173 }
174
175 static gboolean
gst_rtp_theora_pay_setcaps(GstRTPBasePayload * basepayload,GstCaps * caps)176 gst_rtp_theora_pay_setcaps (GstRTPBasePayload * basepayload, GstCaps * caps)
177 {
178 GstRtpTheoraPay *rtptheorapay;
179 GstStructure *s;
180 const GValue *array;
181 gint asize, i;
182 GstBuffer *buf;
183 GstMapInfo map;
184
185 rtptheorapay = GST_RTP_THEORA_PAY (basepayload);
186
187 s = gst_caps_get_structure (caps, 0);
188
189 rtptheorapay->need_headers = TRUE;
190
191 if ((array = gst_structure_get_value (s, "streamheader")) == NULL)
192 goto done;
193
194 if (G_VALUE_TYPE (array) != GST_TYPE_ARRAY)
195 goto done;
196
197 if ((asize = gst_value_array_get_size (array)) < 3)
198 goto done;
199
200 for (i = 0; i < asize; i++) {
201 const GValue *value;
202
203 value = gst_value_array_get_value (array, i);
204 if ((buf = gst_value_get_buffer (value)) == NULL)
205 goto null_buffer;
206
207 gst_buffer_map (buf, &map, GST_MAP_READ);
208 /* no data packets allowed */
209 if (map.size < 1)
210 goto invalid_streamheader;
211
212 /* we need packets with id 0x80, 0x81, 0x82 */
213 if (map.data[0] != 0x80 + i)
214 goto invalid_streamheader;
215
216 if (i == 0) {
217 /* identification, we need to parse this in order to get the clock rate. */
218 if (G_UNLIKELY (!gst_rtp_theora_pay_parse_id (basepayload, map.data,
219 map.size)))
220 goto parse_id_failed;
221 }
222 GST_DEBUG_OBJECT (rtptheorapay, "collecting header %d", i);
223 rtptheorapay->headers =
224 g_list_append (rtptheorapay->headers, gst_buffer_ref (buf));
225 gst_buffer_unmap (buf, &map);
226 }
227 if (!gst_rtp_theora_pay_finish_headers (basepayload))
228 goto finish_failed;
229
230 done:
231 return TRUE;
232
233 /* ERRORS */
234 null_buffer:
235 {
236 GST_WARNING_OBJECT (rtptheorapay, "streamheader with null buffer received");
237 return FALSE;
238 }
239 invalid_streamheader:
240 {
241 GST_WARNING_OBJECT (rtptheorapay, "unable to parse initial header");
242 gst_buffer_unmap (buf, &map);
243 return FALSE;
244 }
245 parse_id_failed:
246 {
247 GST_WARNING_OBJECT (rtptheorapay, "unable to parse initial header");
248 gst_buffer_unmap (buf, &map);
249 return FALSE;
250 }
251 finish_failed:
252 {
253 GST_WARNING_OBJECT (rtptheorapay, "unable to finish headers");
254 return FALSE;
255 }
256 }
257
258 static void
gst_rtp_theora_pay_reset_packet(GstRtpTheoraPay * rtptheorapay,guint8 TDT)259 gst_rtp_theora_pay_reset_packet (GstRtpTheoraPay * rtptheorapay, guint8 TDT)
260 {
261 guint payload_len;
262 GstRTPBuffer rtp = { NULL };
263
264 GST_DEBUG_OBJECT (rtptheorapay, "reset packet");
265
266 rtptheorapay->payload_pos = 4;
267 gst_rtp_buffer_map (rtptheorapay->packet, GST_MAP_READ, &rtp);
268 payload_len = gst_rtp_buffer_get_payload_len (&rtp);
269 gst_rtp_buffer_unmap (&rtp);
270 rtptheorapay->payload_left = payload_len - 4;
271 rtptheorapay->payload_duration = 0;
272 rtptheorapay->payload_F = 0;
273 rtptheorapay->payload_TDT = TDT;
274 rtptheorapay->payload_pkts = 0;
275 }
276
277 static void
gst_rtp_theora_pay_init_packet(GstRtpTheoraPay * rtptheorapay,guint8 TDT,GstClockTime timestamp)278 gst_rtp_theora_pay_init_packet (GstRtpTheoraPay * rtptheorapay, guint8 TDT,
279 GstClockTime timestamp)
280 {
281 GST_DEBUG_OBJECT (rtptheorapay, "starting new packet, TDT: %d", TDT);
282
283 gst_rtp_theora_pay_clear_packet (rtptheorapay);
284
285 /* new packet allocate max packet size */
286 rtptheorapay->packet =
287 gst_rtp_base_payload_allocate_output_buffer (GST_RTP_BASE_PAYLOAD
288 (rtptheorapay), GST_RTP_BASE_PAYLOAD_MTU (rtptheorapay), 0, 0);
289 gst_rtp_theora_pay_reset_packet (rtptheorapay, TDT);
290
291 GST_BUFFER_PTS (rtptheorapay->packet) = timestamp;
292 }
293
294 static GstFlowReturn
gst_rtp_theora_pay_flush_packet(GstRtpTheoraPay * rtptheorapay)295 gst_rtp_theora_pay_flush_packet (GstRtpTheoraPay * rtptheorapay)
296 {
297 GstFlowReturn ret;
298 guint8 *payload;
299 guint hlen;
300 GstRTPBuffer rtp = { NULL };
301 GList *l;
302
303 /* check for empty packet */
304 if (!rtptheorapay->packet || rtptheorapay->payload_pos <= 4)
305 return GST_FLOW_OK;
306
307 GST_DEBUG_OBJECT (rtptheorapay, "flushing packet");
308
309 gst_rtp_buffer_map (rtptheorapay->packet, GST_MAP_WRITE, &rtp);
310
311 /* fix header */
312 payload = gst_rtp_buffer_get_payload (&rtp);
313 /*
314 * 0 1 2 3
315 * 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
316 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
317 * | Ident | F |TDT|# pkts.|
318 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
319 *
320 * F: Fragment type (0=none, 1=start, 2=cont, 3=end)
321 * TDT: Theora data type (0=theora, 1=config, 2=comment, 3=reserved)
322 * pkts: number of packets.
323 */
324 payload[0] = (rtptheorapay->payload_ident >> 16) & 0xff;
325 payload[1] = (rtptheorapay->payload_ident >> 8) & 0xff;
326 payload[2] = (rtptheorapay->payload_ident) & 0xff;
327 payload[3] = (rtptheorapay->payload_F & 0x3) << 6 |
328 (rtptheorapay->payload_TDT & 0x3) << 4 |
329 (rtptheorapay->payload_pkts & 0xf);
330
331 gst_rtp_buffer_unmap (&rtp);
332
333 /* shrink the buffer size to the last written byte */
334 hlen = gst_rtp_buffer_calc_header_len (0);
335 gst_buffer_resize (rtptheorapay->packet, 0, hlen + rtptheorapay->payload_pos);
336
337 GST_BUFFER_DURATION (rtptheorapay->packet) = rtptheorapay->payload_duration;
338
339 for (l = g_list_last (rtptheorapay->packet_buffers); l; l = l->prev) {
340 GstBuffer *buf = GST_BUFFER_CAST (l->data);
341 gst_rtp_copy_video_meta (rtptheorapay, rtptheorapay->packet, buf);
342 gst_buffer_unref (buf);
343 }
344 g_list_free (rtptheorapay->packet_buffers);
345 rtptheorapay->packet_buffers = NULL;
346
347 /* push, this gives away our ref to the packet, so clear it. */
348 ret =
349 gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtptheorapay),
350 rtptheorapay->packet);
351 rtptheorapay->packet = NULL;
352
353 return ret;
354 }
355
356 static gboolean
gst_rtp_theora_pay_finish_headers(GstRTPBasePayload * basepayload)357 gst_rtp_theora_pay_finish_headers (GstRTPBasePayload * basepayload)
358 {
359 GstRtpTheoraPay *rtptheorapay = GST_RTP_THEORA_PAY (basepayload);
360 GList *walk;
361 guint length, size, n_headers, configlen, extralen;
362 gchar *wstr, *hstr, *configuration;
363 guint8 *data, *config;
364 guint32 ident;
365 gboolean res;
366 const gchar *sampling = NULL;
367
368 GST_DEBUG_OBJECT (rtptheorapay, "finish headers");
369
370 if (!rtptheorapay->headers) {
371 GST_DEBUG_OBJECT (rtptheorapay, "We need 2 headers but have none");
372 goto no_headers;
373 }
374
375 /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
376 * | Number of packed headers |
377 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
378 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
379 * | Packed header |
380 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
381 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
382 * | Packed header |
383 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
384 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
385 * | .... |
386 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
387 *
388 * We only construct a config containing 1 packed header like this:
389 *
390 * 0 1 2 3
391 * 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
392 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
393 * | Ident | length ..
394 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
395 * .. | n. of headers | length1 | length2 ..
396 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
397 * .. | Identification Header ..
398 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
399 * .................................................................
400 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
401 * .. | Comment Header ..
402 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
403 * .................................................................
404 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
405 * .. Comment Header |
406 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
407 * | Setup Header ..
408 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
409 * .................................................................
410 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
411 * .. Setup Header |
412 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
413 */
414
415 /* we need 4 bytes for the number of headers (which is always 1), 3 bytes for
416 * the ident, 2 bytes for length, 1 byte for n. of headers. */
417 size = 4 + 3 + 2 + 1;
418
419 /* count the size of the headers first and update the hash */
420 length = 0;
421 n_headers = 0;
422 ident = fnv1_hash_32_new ();
423 extralen = 1;
424 for (walk = rtptheorapay->headers; walk; walk = g_list_next (walk)) {
425 GstBuffer *buf = GST_BUFFER_CAST (walk->data);
426 GstMapInfo map;
427 guint bsize;
428
429 bsize = gst_buffer_get_size (buf);
430 length += bsize;
431 n_headers++;
432
433 /* count number of bytes needed for length fields, we don't need this for
434 * the last header. */
435 if (g_list_next (walk)) {
436 do {
437 size++;
438 extralen++;
439 bsize >>= 7;
440 } while (bsize);
441 }
442 /* update hash */
443 gst_buffer_map (buf, &map, GST_MAP_READ);
444 ident = fnv1_hash_32_update (ident, map.data, map.size);
445 gst_buffer_unmap (buf, &map);
446 }
447
448 /* packet length is header size + packet length */
449 configlen = size + length;
450 config = data = g_malloc (configlen);
451
452 /* number of packed headers, we only pack 1 header */
453 data[0] = 0;
454 data[1] = 0;
455 data[2] = 0;
456 data[3] = 1;
457
458 ident = fnv1_hash_32_to_24 (ident);
459 rtptheorapay->payload_ident = ident;
460 GST_DEBUG_OBJECT (rtptheorapay, "ident 0x%08x", ident);
461
462 /* take lower 3 bytes */
463 data[4] = (ident >> 16) & 0xff;
464 data[5] = (ident >> 8) & 0xff;
465 data[6] = ident & 0xff;
466
467 /* store length of all theora headers */
468 data[7] = ((length) >> 8) & 0xff;
469 data[8] = (length) & 0xff;
470
471 /* store number of headers minus one. */
472 data[9] = n_headers - 1;
473 data += 10;
474
475 /* store length for each header */
476 for (walk = rtptheorapay->headers; walk; walk = g_list_next (walk)) {
477 GstBuffer *buf = GST_BUFFER_CAST (walk->data);
478 guint bsize, size, temp;
479 guint flag;
480
481 /* only need to store the length when it's not the last header */
482 if (!g_list_next (walk))
483 break;
484
485 bsize = gst_buffer_get_size (buf);
486
487 /* calc size */
488 size = 0;
489 do {
490 size++;
491 bsize >>= 7;
492 } while (bsize);
493 temp = size;
494
495 bsize = gst_buffer_get_size (buf);
496 /* write the size backwards */
497 flag = 0;
498 while (size) {
499 size--;
500 data[size] = (bsize & 0x7f) | flag;
501 bsize >>= 7;
502 flag = 0x80; /* Flag bit on all bytes of the length except the last */
503 }
504 data += temp;
505 }
506
507 /* copy header data */
508 for (walk = rtptheorapay->headers; walk; walk = g_list_next (walk)) {
509 GstBuffer *buf = GST_BUFFER_CAST (walk->data);
510
511 gst_buffer_extract (buf, 0, data, gst_buffer_get_size (buf));
512 data += gst_buffer_get_size (buf);
513 }
514 rtptheorapay->need_headers = FALSE;
515
516 /* serialize to base64 */
517 configuration = g_base64_encode (config, configlen);
518
519 /* store for later re-sending */
520 g_free (rtptheorapay->config_data);
521 rtptheorapay->config_size = configlen - 4 - 3 - 2;
522 rtptheorapay->config_data = g_malloc (rtptheorapay->config_size);
523 rtptheorapay->config_extra_len = extralen;
524 memcpy (rtptheorapay->config_data, config + 4 + 3 + 2,
525 rtptheorapay->config_size);
526
527 g_free (config);
528
529 /* configure payloader settings */
530 switch (rtptheorapay->pixel_format) {
531 case 2:
532 sampling = "YCbCr-4:2:2";
533 break;
534 case 3:
535 sampling = "YCbCr-4:4:4";
536 break;
537 case 0:
538 default:
539 sampling = "YCbCr-4:2:0";
540 break;
541 }
542
543
544 wstr = g_strdup_printf ("%d", rtptheorapay->width);
545 hstr = g_strdup_printf ("%d", rtptheorapay->height);
546 gst_rtp_base_payload_set_options (basepayload, "video", TRUE, "THEORA",
547 90000);
548 res =
549 gst_rtp_base_payload_set_outcaps (basepayload, "sampling", G_TYPE_STRING,
550 sampling, "width", G_TYPE_STRING, wstr, "height", G_TYPE_STRING,
551 hstr, "configuration", G_TYPE_STRING, configuration, "delivery-method",
552 G_TYPE_STRING, "inline",
553 /* don't set the other defaults
554 */
555 NULL);
556 g_free (wstr);
557 g_free (hstr);
558 g_free (configuration);
559
560 return res;
561
562 /* ERRORS */
563 no_headers:
564 {
565 GST_DEBUG_OBJECT (rtptheorapay, "finish headers");
566 return FALSE;
567 }
568 }
569
570 static gboolean
gst_rtp_theora_pay_parse_id(GstRTPBasePayload * basepayload,guint8 * data,guint size)571 gst_rtp_theora_pay_parse_id (GstRTPBasePayload * basepayload, guint8 * data,
572 guint size)
573 {
574 GstRtpTheoraPay *rtptheorapay;
575 gint width, height, pixel_format;
576
577 rtptheorapay = GST_RTP_THEORA_PAY (basepayload);
578
579 if (G_UNLIKELY (size < 42))
580 goto too_short;
581
582 if (G_UNLIKELY (memcmp (data, "\200theora", 7)))
583 goto invalid_start;
584 data += 7;
585
586 if (G_UNLIKELY (data[0] != 3))
587 goto invalid_version;
588 if (G_UNLIKELY (data[1] != 2))
589 goto invalid_version;
590 data += 3;
591
592 width = GST_READ_UINT16_BE (data) << 4;
593 data += 2;
594 height = GST_READ_UINT16_BE (data) << 4;
595 data += 29;
596
597 pixel_format = (GST_READ_UINT8 (data) >> 3) & 0x03;
598
599 /* store values */
600 rtptheorapay->pixel_format = pixel_format;
601 rtptheorapay->width = width;
602 rtptheorapay->height = height;
603
604 return TRUE;
605
606 /* ERRORS */
607 too_short:
608 {
609 GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
610 (NULL),
611 ("Identification packet is too short, need at least 42, got %d", size));
612 return FALSE;
613 }
614 invalid_start:
615 {
616 GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
617 (NULL), ("Invalid header start in identification packet"));
618 return FALSE;
619 }
620 invalid_version:
621 {
622 GST_ELEMENT_ERROR (basepayload, STREAM, DECODE,
623 (NULL), ("Invalid version"));
624 return FALSE;
625 }
626 }
627
628 static GstFlowReturn
gst_rtp_theora_pay_payload_buffer(GstRtpTheoraPay * rtptheorapay,guint8 TDT,GstBuffer * buffer,guint8 * data,guint size,GstClockTime timestamp,GstClockTime duration,guint not_in_length)629 gst_rtp_theora_pay_payload_buffer (GstRtpTheoraPay * rtptheorapay, guint8 TDT,
630 GstBuffer * buffer, guint8 * data, guint size, GstClockTime timestamp,
631 GstClockTime duration, guint not_in_length)
632 {
633 GstFlowReturn ret = GST_FLOW_OK;
634 guint newsize;
635 guint packet_len;
636 GstClockTime newduration;
637 gboolean flush;
638 guint plen;
639 guint8 *ppos, *payload;
640 gboolean fragmented;
641 GstRTPBuffer rtp = { NULL };
642
643 /* size increases with packet length and 2 bytes size eader. */
644 newduration = rtptheorapay->payload_duration;
645 if (duration != GST_CLOCK_TIME_NONE)
646 newduration += duration;
647
648 newsize = rtptheorapay->payload_pos + 2 + size;
649 packet_len = gst_rtp_buffer_calc_packet_len (newsize, 0, 0);
650
651 /* check buffer filled against length and max latency */
652 flush = gst_rtp_base_payload_is_filled (GST_RTP_BASE_PAYLOAD (rtptheorapay),
653 packet_len, newduration);
654 /* we can store up to 15 theora packets in one RTP packet. */
655 flush |= (rtptheorapay->payload_pkts == 15);
656 /* flush if we have a new TDT */
657 if (rtptheorapay->packet)
658 flush |= (rtptheorapay->payload_TDT != TDT);
659 if (flush)
660 ret = gst_rtp_theora_pay_flush_packet (rtptheorapay);
661
662 if (ret != GST_FLOW_OK)
663 goto done;
664
665 /* create new packet if we must */
666 if (!rtptheorapay->packet) {
667 gst_rtp_theora_pay_init_packet (rtptheorapay, TDT, timestamp);
668 }
669
670 gst_rtp_buffer_map (rtptheorapay->packet, GST_MAP_WRITE, &rtp);
671 payload = gst_rtp_buffer_get_payload (&rtp);
672 ppos = payload + rtptheorapay->payload_pos;
673 fragmented = FALSE;
674
675 /* put buffer in packet, it either fits completely or needs to be fragmented
676 * over multiple RTP packets. */
677 do {
678 plen = MIN (rtptheorapay->payload_left - 2, size);
679
680 GST_DEBUG_OBJECT (rtptheorapay, "append %u bytes", plen);
681
682 /* data is copied in the payload with a 2 byte length header */
683 ppos[0] = ((plen - not_in_length) >> 8) & 0xff;
684 ppos[1] = ((plen - not_in_length) & 0xff);
685 if (plen)
686 memcpy (&ppos[2], data, plen);
687
688 if (buffer) {
689 if (!rtptheorapay->packet_buffers
690 || rtptheorapay->packet_buffers->data != (gpointer) buffer)
691 rtptheorapay->packet_buffers =
692 g_list_prepend (rtptheorapay->packet_buffers,
693 gst_buffer_ref (buffer));
694 } else {
695 GList *l;
696
697 for (l = rtptheorapay->headers; l; l = l->next)
698 rtptheorapay->packet_buffers =
699 g_list_prepend (rtptheorapay->packet_buffers,
700 gst_buffer_ref (l->data));
701 }
702
703 /* only first (only) configuration cuts length field */
704 /* NOTE: spec (if any) is not clear on this ... */
705 not_in_length = 0;
706
707 size -= plen;
708 data += plen;
709
710 rtptheorapay->payload_pos += plen + 2;
711 rtptheorapay->payload_left -= plen + 2;
712
713 if (fragmented) {
714 if (size == 0)
715 /* last fragment, set F to 0x3. */
716 rtptheorapay->payload_F = 0x3;
717 else
718 /* fragment continues, set F to 0x2. */
719 rtptheorapay->payload_F = 0x2;
720 } else {
721 if (size > 0) {
722 /* fragmented packet starts, set F to 0x1, mark ourselves as
723 * fragmented. */
724 rtptheorapay->payload_F = 0x1;
725 fragmented = TRUE;
726 }
727 }
728 if (fragmented) {
729 gst_rtp_buffer_unmap (&rtp);
730 /* fragmented packets are always flushed and have ptks of 0 */
731 rtptheorapay->payload_pkts = 0;
732 ret = gst_rtp_theora_pay_flush_packet (rtptheorapay);
733
734 if (size > 0) {
735 /* start new packet and get pointers. TDT stays the same. */
736 gst_rtp_theora_pay_init_packet (rtptheorapay,
737 rtptheorapay->payload_TDT, timestamp);
738 gst_rtp_buffer_map (rtptheorapay->packet, GST_MAP_WRITE, &rtp);
739 payload = gst_rtp_buffer_get_payload (&rtp);
740 ppos = payload + rtptheorapay->payload_pos;
741 }
742 } else {
743 /* unfragmented packet, update stats for next packet, size == 0 and we
744 * exit the while loop */
745 rtptheorapay->payload_pkts++;
746 if (duration != GST_CLOCK_TIME_NONE)
747 rtptheorapay->payload_duration += duration;
748 }
749 } while (size && ret == GST_FLOW_OK);
750
751 if (rtp.buffer)
752 gst_rtp_buffer_unmap (&rtp);
753 done:
754
755 return ret;
756 }
757
758 static GstFlowReturn
gst_rtp_theora_pay_handle_buffer(GstRTPBasePayload * basepayload,GstBuffer * buffer)759 gst_rtp_theora_pay_handle_buffer (GstRTPBasePayload * basepayload,
760 GstBuffer * buffer)
761 {
762 GstRtpTheoraPay *rtptheorapay;
763 GstFlowReturn ret;
764 GstMapInfo map;
765 gsize size;
766 guint8 *data;
767 GstClockTime duration, timestamp;
768 guint8 TDT;
769 gboolean keyframe = FALSE;
770
771 rtptheorapay = GST_RTP_THEORA_PAY (basepayload);
772
773 gst_buffer_map (buffer, &map, GST_MAP_READ);
774 data = map.data;
775 size = map.size;
776 duration = GST_BUFFER_DURATION (buffer);
777 timestamp = GST_BUFFER_PTS (buffer);
778
779 GST_DEBUG_OBJECT (rtptheorapay, "size %" G_GSIZE_FORMAT
780 ", duration %" GST_TIME_FORMAT, size, GST_TIME_ARGS (duration));
781
782 /* find packet type */
783 if (size == 0) {
784 TDT = 0;
785 keyframe = FALSE;
786 } else if (data[0] & 0x80) {
787 /* header */
788 if (data[0] == 0x80) {
789 /* identification, we need to parse this in order to get the clock rate.
790 */
791 if (G_UNLIKELY (!gst_rtp_theora_pay_parse_id (basepayload, data, size)))
792 goto parse_id_failed;
793 TDT = 1;
794 } else if (data[0] == 0x81) {
795 /* comment */
796 TDT = 2;
797 } else if (data[0] == 0x82) {
798 /* setup */
799 TDT = 1;
800 } else
801 goto unknown_header;
802 } else {
803 /* data */
804 TDT = 0;
805 keyframe = ((data[0] & 0x40) == 0);
806 }
807
808 /* we need to collect the headers and construct a config string from them */
809 if (TDT != 0) {
810 GST_DEBUG_OBJECT (rtptheorapay, "collecting header, buffer %p", buffer);
811 /* append header to the list of headers */
812 gst_buffer_unmap (buffer, &map);
813 rtptheorapay->headers = g_list_append (rtptheorapay->headers, buffer);
814 ret = GST_FLOW_OK;
815 goto done;
816 } else if (rtptheorapay->headers && rtptheorapay->need_headers) {
817 if (!gst_rtp_theora_pay_finish_headers (basepayload))
818 goto header_error;
819 }
820
821 /* there is a config request, see if we need to insert it */
822 if (keyframe && (rtptheorapay->config_interval > 0) &&
823 rtptheorapay->config_data) {
824 gboolean send_config = FALSE;
825 GstClockTime running_time =
826 gst_segment_to_running_time (&basepayload->segment, GST_FORMAT_TIME,
827 timestamp);
828
829 if (rtptheorapay->last_config != -1) {
830 guint64 diff;
831
832 GST_LOG_OBJECT (rtptheorapay,
833 "now %" GST_TIME_FORMAT ", last VOP-I %" GST_TIME_FORMAT,
834 GST_TIME_ARGS (running_time),
835 GST_TIME_ARGS (rtptheorapay->last_config));
836
837 /* calculate diff between last config in milliseconds */
838 if (running_time > rtptheorapay->last_config) {
839 diff = running_time - rtptheorapay->last_config;
840 } else {
841 diff = 0;
842 }
843
844 GST_DEBUG_OBJECT (rtptheorapay,
845 "interval since last config %" GST_TIME_FORMAT, GST_TIME_ARGS (diff));
846
847 /* bigger than interval, queue config */
848 if (GST_TIME_AS_SECONDS (diff) >= rtptheorapay->config_interval) {
849 GST_DEBUG_OBJECT (rtptheorapay, "time to send config");
850 send_config = TRUE;
851 }
852 } else {
853 /* no known previous config time, send now */
854 GST_DEBUG_OBJECT (rtptheorapay, "no previous config time, send now");
855 send_config = TRUE;
856 }
857
858 if (send_config) {
859 /* we need to send config now first */
860 /* different TDT type forces flush */
861 gst_rtp_theora_pay_payload_buffer (rtptheorapay, 1,
862 NULL, rtptheorapay->config_data, rtptheorapay->config_size,
863 timestamp, GST_CLOCK_TIME_NONE, rtptheorapay->config_extra_len);
864
865 if (running_time != -1) {
866 rtptheorapay->last_config = running_time;
867 }
868 }
869 }
870
871 ret =
872 gst_rtp_theora_pay_payload_buffer (rtptheorapay, TDT, buffer, data, size,
873 timestamp, duration, 0);
874
875 gst_buffer_unmap (buffer, &map);
876 gst_buffer_unref (buffer);
877
878 done:
879 return ret;
880
881 /* ERRORS */
882 parse_id_failed:
883 {
884 gst_buffer_unmap (buffer, &map);
885 gst_buffer_unref (buffer);
886 return GST_FLOW_ERROR;
887 }
888 unknown_header:
889 {
890 GST_ELEMENT_WARNING (rtptheorapay, STREAM, DECODE,
891 (NULL), ("Ignoring unknown header received"));
892 gst_buffer_unmap (buffer, &map);
893 gst_buffer_unref (buffer);
894 return GST_FLOW_OK;
895 }
896 header_error:
897 {
898 GST_ELEMENT_WARNING (rtptheorapay, STREAM, DECODE,
899 (NULL), ("Error initializing header config"));
900 gst_buffer_unmap (buffer, &map);
901 gst_buffer_unref (buffer);
902 return GST_FLOW_OK;
903 }
904 }
905
906 static gboolean
gst_rtp_theora_pay_sink_event(GstRTPBasePayload * payload,GstEvent * event)907 gst_rtp_theora_pay_sink_event (GstRTPBasePayload * payload, GstEvent * event)
908 {
909 GstRtpTheoraPay *rtptheorapay = GST_RTP_THEORA_PAY (payload);
910
911 switch (GST_EVENT_TYPE (event)) {
912 case GST_EVENT_FLUSH_STOP:
913 gst_rtp_theora_pay_clear_packet (rtptheorapay);
914 break;
915 default:
916 break;
917 }
918 /* false to let parent handle event as well */
919 return GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (payload, event);
920 }
921
922 static GstStateChangeReturn
gst_rtp_theora_pay_change_state(GstElement * element,GstStateChange transition)923 gst_rtp_theora_pay_change_state (GstElement * element,
924 GstStateChange transition)
925 {
926 GstRtpTheoraPay *rtptheorapay;
927
928 GstStateChangeReturn ret;
929
930 rtptheorapay = GST_RTP_THEORA_PAY (element);
931
932 switch (transition) {
933 default:
934 break;
935 }
936
937 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
938
939 switch (transition) {
940 case GST_STATE_CHANGE_PAUSED_TO_READY:
941 gst_rtp_theora_pay_cleanup (rtptheorapay);
942 break;
943 default:
944 break;
945 }
946 return ret;
947 }
948
949 static void
gst_rtp_theora_pay_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)950 gst_rtp_theora_pay_set_property (GObject * object, guint prop_id,
951 const GValue * value, GParamSpec * pspec)
952 {
953 GstRtpTheoraPay *rtptheorapay;
954
955 rtptheorapay = GST_RTP_THEORA_PAY (object);
956
957 switch (prop_id) {
958 case PROP_CONFIG_INTERVAL:
959 rtptheorapay->config_interval = g_value_get_uint (value);
960 break;
961 default:
962 break;
963 }
964 }
965
966 static void
gst_rtp_theora_pay_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)967 gst_rtp_theora_pay_get_property (GObject * object, guint prop_id,
968 GValue * value, GParamSpec * pspec)
969 {
970 GstRtpTheoraPay *rtptheorapay;
971
972 rtptheorapay = GST_RTP_THEORA_PAY (object);
973
974 switch (prop_id) {
975 case PROP_CONFIG_INTERVAL:
976 g_value_set_uint (value, rtptheorapay->config_interval);
977 break;
978 default:
979 break;
980 }
981 }
982