2 * Copyright (C) <2006> Wim Taymans <wim.taymans@gmail.com>
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.
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.
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.
24 #include <gst/tag/tag.h>
25 #include <gst/rtp/gstrtpbuffer.h>
28 #include "gstrtptheoradepay.h"
30 GST_DEBUG_CATEGORY_STATIC (rtptheoradepay_debug);
31 #define GST_CAT_DEFAULT (rtptheoradepay_debug)
33 static GstStaticPadTemplate gst_rtp_theora_depay_sink_template =
34 GST_STATIC_PAD_TEMPLATE ("sink",
37 GST_STATIC_CAPS ("application/x-rtp, "
38 "media = (string) \"video\", "
39 "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
40 "clock-rate = (int) 90000, " "encoding-name = (string) \"THEORA\""
41 /* All required parameters
43 * "sampling = (string) { "YCbCr-4:2:0", "YCbCr-4:2:2", "YCbCr-4:4:4" } "
44 * "width = (string) [1, 1048561] (multiples of 16) "
45 * "height = (string) [1, 1048561] (multiples of 16) "
46 * "delivery-method = (string) { inline, in_band, out_band/<specific_name> } "
47 * "configuration = (string) ANY"
49 /* All optional parameters
51 * "configuration-uri ="
56 static GstStaticPadTemplate gst_rtp_theora_depay_src_template =
57 GST_STATIC_PAD_TEMPLATE ("src",
60 GST_STATIC_CAPS ("video/x-theora")
63 #define gst_rtp_theora_depay_parent_class parent_class
64 G_DEFINE_TYPE (GstRtpTheoraDepay, gst_rtp_theora_depay,
65 GST_TYPE_RTP_BASE_DEPAYLOAD);
67 static gboolean gst_rtp_theora_depay_setcaps (GstRTPBaseDepayload * depayload,
69 static GstBuffer *gst_rtp_theora_depay_process (GstRTPBaseDepayload * depayload,
71 static gboolean gst_rtp_theora_depay_packet_lost (GstRTPBaseDepayload *
72 depayload, GstEvent * event);
74 static void gst_rtp_theora_depay_finalize (GObject * object);
77 gst_rtp_theora_depay_class_init (GstRtpTheoraDepayClass * klass)
79 GObjectClass *gobject_class;
80 GstElementClass *gstelement_class;
81 GstRTPBaseDepayloadClass *gstrtpbasedepayload_class;
83 gobject_class = (GObjectClass *) klass;
84 gstelement_class = (GstElementClass *) klass;
85 gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass;
87 gobject_class->finalize = gst_rtp_theora_depay_finalize;
89 gstrtpbasedepayload_class->process = gst_rtp_theora_depay_process;
90 gstrtpbasedepayload_class->set_caps = gst_rtp_theora_depay_setcaps;
91 gstrtpbasedepayload_class->packet_lost = gst_rtp_theora_depay_packet_lost;
93 gst_element_class_add_pad_template (gstelement_class,
94 gst_static_pad_template_get (&gst_rtp_theora_depay_sink_template));
95 gst_element_class_add_pad_template (gstelement_class,
96 gst_static_pad_template_get (&gst_rtp_theora_depay_src_template));
98 gst_element_class_set_details_simple (gstelement_class,
99 "RTP Theora depayloader", "Codec/Depayloader/Network/RTP",
100 "Extracts Theora video from RTP packets (draft-01 of RFC XXXX)",
101 "Wim Taymans <wim.taymans@gmail.com>");
103 GST_DEBUG_CATEGORY_INIT (rtptheoradepay_debug, "rtptheoradepay", 0,
104 "Theora RTP Depayloader");
108 gst_rtp_theora_depay_init (GstRtpTheoraDepay * rtptheoradepay)
110 rtptheoradepay->adapter = gst_adapter_new ();
114 gst_rtp_theora_depay_finalize (GObject * object)
116 GstRtpTheoraDepay *rtptheoradepay = GST_RTP_THEORA_DEPAY (object);
118 g_object_unref (rtptheoradepay->adapter);
120 G_OBJECT_CLASS (parent_class)->finalize (object);
124 gst_rtp_theora_depay_parse_configuration (GstRtpTheoraDepay * rtptheoradepay,
129 guint8 *data, *bdata;
133 data = bdata = gst_buffer_map (confbuf, &size, NULL, GST_MAP_READ);
135 GST_DEBUG_OBJECT (rtptheoradepay, "config size %" G_GSIZE_FORMAT, size);
137 /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
138 * | Number of packed headers |
139 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
140 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
142 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
143 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
145 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
146 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
148 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
153 num_headers = GST_READ_UINT32_BE (data);
157 GST_DEBUG_OBJECT (rtptheoradepay, "have %u headers", num_headers);
160 * 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
161 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
162 * | Ident | length ..
163 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
164 * .. | n. of headers | length1 | length2 ..
165 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
166 * .. | Identification Header ..
167 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
168 * .................................................................
169 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
170 * .. | Comment Header ..
171 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
172 * .................................................................
173 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
174 * .. Comment Header |
175 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
177 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
178 * .................................................................
179 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
181 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
183 for (i = 0; i < num_headers; i++) {
187 GstRtpTheoraConfig *conf;
194 ident = (data[0] << 16) | (data[1] << 8) | data[2];
195 length = (data[3] << 8) | data[4];
200 GST_DEBUG_OBJECT (rtptheoradepay,
201 "header %d, ident 0x%08x, length %u, left %" G_GSIZE_FORMAT, i, ident,
204 /* FIXME check if we already got this ident */
206 /* length might also include count of following size fields */
207 if (size < length && size + 1 != length)
210 /* read header sizes we read 2 sizes, the third size (for which we allocate
211 * space) must be derived from the total packed header length. */
212 h_sizes = g_newa (guint, n_headers + 1);
213 for (j = 0; j < n_headers; j++) {
223 h_size = (h_size << 7) | (b & 0x7f);
225 GST_DEBUG_OBJECT (rtptheoradepay, "headers %d: size: %u", j, h_size);
229 /* last header length is the remaining space */
230 GST_DEBUG_OBJECT (rtptheoradepay, "last header size: %u", length);
233 GST_DEBUG_OBJECT (rtptheoradepay, "preparing headers");
234 conf = g_new0 (GstRtpTheoraConfig, 1);
237 for (j = 0; j <= n_headers; j++) {
243 if (j != n_headers || size + extra != h_size) {
246 /* otherwise means that overall length field contained total length,
247 * including extra fields */
252 GST_DEBUG_OBJECT (rtptheoradepay, "reading header %d, size %u", j,
255 buf = gst_buffer_new_and_alloc (h_size);
256 odata = gst_buffer_map (buf, NULL, NULL, GST_MAP_WRITE);
257 memcpy (odata, data, h_size);
258 gst_buffer_unmap (buf, odata, -1);
259 conf->headers = g_list_append (conf->headers, buf);
263 rtptheoradepay->configs = g_list_append (rtptheoradepay->configs, conf);
266 gst_buffer_unmap (confbuf, bdata, -1);
272 GST_DEBUG_OBJECT (rtptheoradepay, "configuration too small");
273 gst_buffer_unmap (confbuf, bdata, -1);
279 gst_rtp_theora_depay_parse_inband_configuration (GstRtpTheoraDepay *
280 rtptheoradepay, guint ident, guint8 * configuration, guint size,
286 if (G_UNLIKELY (size < 4))
289 /* transform inline to out-of-band and parse that one */
290 confbuf = gst_buffer_new_and_alloc (size + 9);
291 conf = gst_buffer_map (confbuf, NULL, NULL, GST_MAP_WRITE);
293 GST_WRITE_UINT32_BE (conf, 1);
295 GST_WRITE_UINT24_BE (conf + 4, ident);
296 /* write sort-of-length */
297 GST_WRITE_UINT16_BE (conf + 7, length);
299 memcpy (conf + 9, configuration, size);
300 gst_buffer_unmap (confbuf, conf, -1);
302 return gst_rtp_theora_depay_parse_configuration (rtptheoradepay, confbuf);
306 gst_rtp_theora_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
308 GstStructure *structure;
309 GstRtpTheoraDepay *rtptheoradepay;
311 const gchar *configuration;
314 rtptheoradepay = GST_RTP_THEORA_DEPAY (depayload);
316 rtptheoradepay->needs_keyframe = FALSE;
318 structure = gst_caps_get_structure (caps, 0);
320 /* read and parse configuration string */
321 configuration = gst_structure_get_string (structure, "configuration");
327 /* configure string should be in the caps */
328 if (configuration == NULL)
329 goto no_configuration;
331 /* deserialize base64 to buffer */
332 data = g_base64_decode (configuration, &size);
334 confbuf = gst_buffer_new ();
335 gst_buffer_take_memory (confbuf, -1,
336 gst_memory_new_wrapped (0, data, g_free, size, 0, size));
338 if (!gst_rtp_theora_depay_parse_configuration (rtptheoradepay, confbuf))
339 goto invalid_configuration;
342 /* set caps on pad and on header */
343 srccaps = gst_caps_new_empty_simple ("video/x-theora");
344 res = gst_pad_set_caps (depayload->srcpad, srccaps);
345 gst_caps_unref (srccaps);
347 /* Clock rate is always 90000 according to draft-barbato-avt-rtp-theora-01 */
348 depayload->clock_rate = 90000;
355 GST_ERROR_OBJECT (rtptheoradepay, "no configuration specified");
358 invalid_configuration:
360 GST_ERROR_OBJECT (rtptheoradepay, "invalid configuration specified");
366 gst_rtp_theora_depay_switch_codebook (GstRtpTheoraDepay * rtptheoradepay,
370 gboolean res = FALSE;
372 for (walk = rtptheoradepay->configs; walk; walk = g_list_next (walk)) {
373 GstRtpTheoraConfig *conf = (GstRtpTheoraConfig *) walk->data;
375 if (conf->ident == ident) {
378 /* FIXME, remove pads, create new pad.. */
380 /* push out all the headers */
381 for (headers = conf->headers; headers; headers = g_list_next (headers)) {
382 GstBuffer *header = GST_BUFFER_CAST (headers->data);
384 gst_buffer_ref (header);
385 gst_rtp_base_depayload_push (GST_RTP_BASE_DEPAYLOAD (rtptheoradepay),
388 /* remember the current config */
389 rtptheoradepay->config = conf;
394 /* we don't know about the headers, figure out an alternative method for
395 * getting the codebooks. FIXME, fail for now. */
401 gst_rtp_theora_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf)
403 GstRtpTheoraDepay *rtptheoradepay;
407 guint8 *payload, *to_free = NULL;
408 guint32 header, ident;
409 guint8 F, TDT, packets;
412 rtptheoradepay = GST_RTP_THEORA_DEPAY (depayload);
414 gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp);
416 payload_len = gst_rtp_buffer_get_payload_len (&rtp);
418 GST_DEBUG_OBJECT (depayload, "got RTP packet of size %d", payload_len);
420 /* we need at least 4 bytes for the packet header */
421 if (G_UNLIKELY (payload_len < 4))
424 payload = gst_rtp_buffer_get_payload (&rtp);
426 header = GST_READ_UINT32_BE (payload);
429 * 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
430 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
431 * | Ident | F |TDT|# pkts.|
432 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
434 * F: Fragment type (0=none, 1=start, 2=cont, 3=end)
435 * TDT: Theora data type (0=theora, 1=config, 2=comment, 3=reserved)
436 * pkts: number of packets.
438 TDT = (header & 0x30) >> 4;
439 if (G_UNLIKELY (TDT == 3))
440 goto ignore_reserved;
442 ident = (header >> 8) & 0xffffff;
443 F = (header & 0xc0) >> 6;
444 packets = (header & 0xf);
446 GST_DEBUG_OBJECT (depayload, "ident: 0x%08x, F: %d, TDT: %d, packets: %d",
447 ident, F, TDT, packets);
450 gboolean do_switch = FALSE;
452 /* we have a raw payload, find the codebook for the ident */
453 if (!rtptheoradepay->config) {
454 /* we don't have an active codebook, find the codebook and
457 } else if (rtptheoradepay->config->ident != ident) {
458 /* codebook changed */
462 if (!gst_rtp_theora_depay_switch_codebook (rtptheoradepay, ident))
471 /* fragmented packets, assemble */
477 /* if we start a packet, clear adapter and start assembling. */
478 gst_adapter_clear (rtptheoradepay->adapter);
479 GST_DEBUG_OBJECT (depayload, "start assemble");
480 rtptheoradepay->assembling = TRUE;
483 if (!rtptheoradepay->assembling)
486 /* first assembled packet, reuse 2 bytes to store the length */
487 headerskip = (F == 1 ? 4 : 6);
488 /* skip header and length. */
489 vdata = gst_rtp_buffer_get_payload_subbuffer (&rtp, headerskip, -1);
491 GST_DEBUG_OBJECT (depayload, "assemble theora packet");
492 gst_adapter_push (rtptheoradepay->adapter, vdata);
494 /* packet is not complete, we are done */
498 /* construct assembled buffer */
499 payload_len = gst_adapter_available (rtptheoradepay->adapter);
500 payload = gst_adapter_take (rtptheoradepay->adapter, payload_len);
502 payload[0] = ((payload_len - 2) >> 8) & 0xff;
503 payload[1] = (payload_len - 2) & 0xff;
507 GST_DEBUG_OBJECT (depayload, "assemble done, payload_len %d", payload_len);
509 /* we not assembling anymore now */
510 rtptheoradepay->assembling = FALSE;
511 gst_adapter_clear (rtptheoradepay->adapter);
513 /* payload now points to a length with that many theora data bytes.
514 * Iterate over the packets and send them out.
517 * 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
518 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
519 * | length | theora data ..
520 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
522 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
523 * | length | next theora packet data ..
524 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
526 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+*
528 while (payload_len >= 2) {
531 length = GST_READ_UINT16_BE (payload);
535 GST_DEBUG_OBJECT (depayload, "read length %u, avail: %d", length,
538 /* skip packet if something odd happens */
539 if (G_UNLIKELY (length > payload_len))
542 /* handle in-band configuration */
543 if (G_UNLIKELY (TDT == 1)) {
544 GST_DEBUG_OBJECT (rtptheoradepay, "in-band configuration");
545 if (!gst_rtp_theora_depay_parse_inband_configuration (rtptheoradepay,
546 ident, payload, payload_len, length))
547 goto invalid_configuration;
551 /* create buffer for packet */
552 if (G_UNLIKELY (to_free)) {
553 outbuf = gst_buffer_new ();
554 gst_buffer_take_memory (buf, -1,
555 gst_memory_new_wrapped (0, to_free, g_free,
556 (payload - to_free) + length, payload - to_free, length));
561 outbuf = gst_buffer_new_and_alloc (length);
562 odata = gst_buffer_map (outbuf, NULL, NULL, GST_MAP_WRITE);
563 memcpy (odata, payload, length);
564 gst_buffer_unmap (outbuf, odata, -1);
567 if (payload_len > 0 && (payload[0] & 0xC0) == 0x0)
568 rtptheoradepay->needs_keyframe = FALSE;
571 payload_len -= length;
573 ret = gst_rtp_base_depayload_push (depayload, outbuf);
574 if (ret != GST_FLOW_OK)
580 if (rtptheoradepay->needs_keyframe)
581 goto request_keyframe;
583 gst_rtp_buffer_unmap (&rtp);
588 gst_rtp_buffer_unmap (&rtp);
594 GST_ELEMENT_WARNING (rtptheoradepay, STREAM, DECODE,
595 (NULL), ("Could not switch codebooks"));
600 GST_ELEMENT_WARNING (rtptheoradepay, STREAM, DECODE,
601 (NULL), ("Packet was too short (%d < 4)", payload_len));
602 goto request_keyframe;
606 GST_WARNING_OBJECT (rtptheoradepay, "reserved TDT ignored");
607 gst_rtp_buffer_unmap (&rtp);
612 GST_ELEMENT_WARNING (rtptheoradepay, STREAM, DECODE,
613 (NULL), ("Packet contains invalid data"));
614 goto request_keyframe;
616 invalid_configuration:
618 /* fatal, as we otherwise risk carrying on without output */
619 GST_ELEMENT_ERROR (rtptheoradepay, STREAM, DECODE,
620 (NULL), ("Packet contains invalid configuration"));
625 gst_pad_push_event (GST_RTP_BASE_DEPAYLOAD_SINKPAD (depayload),
626 gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
627 gst_structure_new ("GstForceKeyUnit",
628 "all-headers", G_TYPE_BOOLEAN, TRUE, NULL)));
629 gst_rtp_buffer_unmap (&rtp);
634 rtptheoradepay->needs_keyframe = TRUE;
635 gst_pad_push_event (GST_RTP_BASE_DEPAYLOAD_SINKPAD (depayload),
636 gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
637 gst_structure_new_empty ("GstForceKeyUnit")));
638 gst_rtp_buffer_unmap (&rtp);
644 gst_rtp_theora_depay_plugin_init (GstPlugin * plugin)
646 return gst_element_register (plugin, "rtptheoradepay",
647 GST_RANK_SECONDARY, GST_TYPE_RTP_THEORA_DEPAY);
651 gst_rtp_theora_depay_packet_lost (GstRTPBaseDepayload * depayload,
654 GstRtpTheoraDepay *rtptheoradepay = GST_RTP_THEORA_DEPAY (depayload);
657 gst_structure_get_uint (gst_event_get_structure (event), "seqnum", &seqnum);
658 GST_LOG_OBJECT (depayload, "Requested keyframe because frame with seqnum %u"
659 " is missing", seqnum);
660 rtptheoradepay->needs_keyframe = TRUE;
662 gst_pad_push_event (GST_RTP_BASE_DEPAYLOAD_SINKPAD (depayload),
663 gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
664 gst_structure_new_empty ("GstForceKeyUnit")));