2
* Copyright (C) <2006> Wim Taymans <wim@fluendo.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
/* elementfactory information */
34
static const GstElementDetails gst_rtp_theora_depay_details =
35
GST_ELEMENT_DETAILS ("RTP packet depayloader",
36
"Codec/Depayloader/Network",
37
"Extracts Theora video from RTP packets (draft-01 of RFC XXXX)",
38
"Wim Taymans <wim@fluendo.com>");
40
/* RtpTheoraDepay signals and args */
52
static GstStaticPadTemplate gst_rtp_theora_depay_sink_template =
53
GST_STATIC_PAD_TEMPLATE ("sink",
56
GST_STATIC_CAPS ("application/x-rtp, "
57
"media = (string) \"video\", "
58
"clock-rate = (int) [1, MAX ], " "encoding-name = (string) \"theora\""
59
/* All required parameters
61
* "encoding-params = (string) <num channels>"
62
* "delivery-method = (string) { inline, in_band, out_band/<specific_name> } "
63
* "configuration = (string) ANY"
65
/* All optional parameters
67
* "configuration-uri ="
72
static GstStaticPadTemplate gst_rtp_theora_depay_src_template =
73
GST_STATIC_PAD_TEMPLATE ("src",
76
GST_STATIC_CAPS ("video/x-theora")
79
/* 42 bytes for the theora identification packet length */
80
#define THEORA_ID_LEN 42
82
GST_BOILERPLATE (GstRtpTheoraDepay, gst_rtp_theora_depay, GstBaseRTPDepayload,
83
GST_TYPE_BASE_RTP_DEPAYLOAD);
85
static gboolean gst_rtp_theora_depay_setcaps (GstBaseRTPDepayload * depayload,
87
static GstBuffer *gst_rtp_theora_depay_process (GstBaseRTPDepayload * depayload,
90
static void gst_rtp_theora_depay_set_property (GObject * object, guint prop_id,
91
const GValue * value, GParamSpec * pspec);
92
static void gst_rtp_theora_depay_get_property (GObject * object, guint prop_id,
93
GValue * value, GParamSpec * pspec);
94
static void gst_rtp_theora_depay_finalize (GObject * object);
96
static GstStateChangeReturn gst_rtp_theora_depay_change_state (GstElement *
97
element, GstStateChange transition);
101
gst_rtp_theora_depay_base_init (gpointer klass)
103
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
105
gst_element_class_add_pad_template (element_class,
106
gst_static_pad_template_get (&gst_rtp_theora_depay_sink_template));
107
gst_element_class_add_pad_template (element_class,
108
gst_static_pad_template_get (&gst_rtp_theora_depay_src_template));
110
gst_element_class_set_details (element_class, &gst_rtp_theora_depay_details);
114
gst_rtp_theora_depay_class_init (GstRtpTheoraDepayClass * klass)
116
GObjectClass *gobject_class;
117
GstElementClass *gstelement_class;
118
GstBaseRTPDepayloadClass *gstbasertpdepayload_class;
120
gobject_class = (GObjectClass *) klass;
121
gstelement_class = (GstElementClass *) klass;
122
gstbasertpdepayload_class = (GstBaseRTPDepayloadClass *) klass;
124
gobject_class->set_property = gst_rtp_theora_depay_set_property;
125
gobject_class->get_property = gst_rtp_theora_depay_get_property;
126
gobject_class->finalize = gst_rtp_theora_depay_finalize;
128
gstelement_class->change_state = gst_rtp_theora_depay_change_state;
130
gstbasertpdepayload_class->process = gst_rtp_theora_depay_process;
131
gstbasertpdepayload_class->set_caps = gst_rtp_theora_depay_setcaps;
133
GST_DEBUG_CATEGORY_INIT (rtptheoradepay_debug, "rtptheoradepay", 0,
134
"Theora RTP Depayloader");
138
gst_rtp_theora_depay_init (GstRtpTheoraDepay * rtptheoradepay,
139
GstRtpTheoraDepayClass * klass)
141
rtptheoradepay->adapter = gst_adapter_new ();
144
gst_rtp_theora_depay_finalize (GObject * object)
146
GstRtpTheoraDepay *rtptheoradepay = GST_RTP_THEORA_DEPAY (object);
148
g_object_unref (rtptheoradepay->adapter);
150
G_OBJECT_CLASS (parent_class)->finalize (object);
154
gst_rtp_theora_depay_parse_configuration (GstRtpTheoraDepay * rtptheoradepay,
155
const gchar * configuration)
164
/* deserialize base16 to buffer */
165
g_value_init (&v, GST_TYPE_BUFFER);
166
if (!gst_value_deserialize (&v, configuration))
167
goto wrong_configuration;
169
buf = gst_value_get_buffer (&v);
170
gst_buffer_ref (buf);
173
data = GST_BUFFER_DATA (buf);
174
size = GST_BUFFER_SIZE (buf);
176
GST_DEBUG_OBJECT (rtptheoradepay, "config size %u", size);
178
/* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
179
* | Number of packed headers |
180
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
181
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
183
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
184
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
186
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
187
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
189
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
194
num_headers = GST_READ_UINT32_BE (data);
198
GST_DEBUG_OBJECT (rtptheoradepay, "have %u headers", num_headers);
201
* 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
202
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
204
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
205
* .. length | Identification Header ..
206
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
207
* .. Identification Header |
208
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
210
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
212
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
215
for (i = 0; i < num_headers; i++) {
218
GstRtpTheoraConfig *conf;
224
ident = (data[0] << 16) | (data[1] << 8) | data[2];
225
length = (data[3] << 8) | data[4];
229
GST_DEBUG_OBJECT (rtptheoradepay, "header %d, ident %08x, length %u", i,
232
if (size < length + THEORA_ID_LEN)
235
GST_DEBUG_OBJECT (rtptheoradepay, "preparing headers");
237
conf = g_new0 (GstRtpTheoraConfig, 1);
240
buf = gst_buffer_new_and_alloc (THEORA_ID_LEN);
241
memcpy (GST_BUFFER_DATA (buf), data, THEORA_ID_LEN);
242
conf->headers = g_list_append (conf->headers, buf);
243
data += THEORA_ID_LEN;
244
size -= THEORA_ID_LEN;
246
/* create a dummy comment */
247
list = gst_tag_list_new ();
249
gst_tag_list_to_vorbiscomment_buffer (list, (guint8 *) "\201theora", 7,
250
"Theora RTP depayloader");
251
conf->headers = g_list_append (conf->headers, buf);
252
gst_tag_list_free (list);
254
buf = gst_buffer_new_and_alloc (length);
255
memcpy (GST_BUFFER_DATA (buf), data, length);
256
conf->headers = g_list_append (conf->headers, buf);
260
rtptheoradepay->configs = g_list_append (rtptheoradepay->configs, conf);
268
GST_DEBUG_OBJECT (rtptheoradepay, "error parsing configuration");
273
GST_DEBUG_OBJECT (rtptheoradepay, "configuration too small");
279
gst_rtp_theora_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps)
281
GstStructure *structure;
282
GstRtpTheoraDepay *rtptheoradepay;
284
const gchar *delivery_method;
285
const gchar *configuration;
288
rtptheoradepay = GST_RTP_THEORA_DEPAY (depayload);
290
structure = gst_caps_get_structure (caps, 0);
293
if (!gst_structure_get_int (structure, "clock-rate", &clock_rate))
296
/* see how the configuration parameters will be transmitted */
297
delivery_method = gst_structure_get_string (structure, "delivery-method");
298
if (delivery_method == NULL)
299
goto no_delivery_method;
301
if (g_strcasecmp (delivery_method, "inline")) {
302
/* configure string is in the caps */
303
} else if (g_strcasecmp (delivery_method, "in_band")) {
304
/* headers will (also) be transmitted in the RTP packets */
305
} else if (g_str_has_prefix (delivery_method, "out_band/")) {
306
/* some other method of header delivery. */
307
goto unsupported_delivery_method;
309
goto unsupported_delivery_method;
311
/* read and parse configuration string */
312
configuration = gst_structure_get_string (structure, "configuration");
313
if (configuration == NULL)
314
goto no_configuration;
316
if (!gst_rtp_theora_depay_parse_configuration (rtptheoradepay, configuration))
317
goto invalid_configuration;
319
/* caps seem good, configure element */
320
depayload->clock_rate = clock_rate;
322
/* set caps on pad and on header */
323
srccaps = gst_caps_new_simple ("video/x-theora", NULL);
324
gst_pad_set_caps (depayload->srcpad, srccaps);
325
gst_caps_unref (srccaps);
330
unsupported_delivery_method:
332
GST_ERROR_OBJECT (rtptheoradepay,
333
"unsupported delivery-method \"%s\" specified", delivery_method);
338
GST_ERROR_OBJECT (rtptheoradepay, "no delivery-method specified");
343
GST_ERROR_OBJECT (rtptheoradepay, "no configuration specified");
346
invalid_configuration:
348
GST_ERROR_OBJECT (rtptheoradepay, "invalid configuration specified");
353
GST_ERROR_OBJECT (rtptheoradepay, "no clock-rate specified");
359
gst_rtp_theora_depay_switch_codebook (GstRtpTheoraDepay * rtptheoradepay,
363
gboolean res = FALSE;
365
for (walk = rtptheoradepay->configs; walk; walk = g_list_next (walk)) {
366
GstRtpTheoraConfig *conf = (GstRtpTheoraConfig *) walk->data;
368
if (conf->ident == ident) {
371
/* FIXME, remove pads, create new pad.. */
373
/* push out all the headers */
374
for (headers = conf->headers; headers; headers = g_list_next (headers)) {
375
GstBuffer *header = GST_BUFFER_CAST (headers->data);
377
gst_buffer_ref (header);
378
gst_base_rtp_depayload_push (GST_BASE_RTP_DEPAYLOAD (rtptheoradepay),
381
/* remember the current config */
382
rtptheoradepay->config = conf;
387
/* we don't know about the headers, figure out an alternative method for
388
* getting the codebooks. FIXME, fail for now. */
394
gst_rtp_theora_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
396
GstRtpTheoraDepay *rtptheoradepay;
400
guint8 *payload, *to_free = NULL;
402
guint32 header, ident;
403
guint8 F, TDT, packets;
404
gboolean free_payload;
406
rtptheoradepay = GST_RTP_THEORA_DEPAY (depayload);
408
if (!gst_rtp_buffer_validate (buf))
411
payload_len = gst_rtp_buffer_get_payload_len (buf);
413
GST_DEBUG_OBJECT (depayload, "got RTP packet of size %d", payload_len);
415
/* we need at least 4 bytes for the packet header */
416
if (G_UNLIKELY (payload_len < 4))
419
payload = gst_rtp_buffer_get_payload (buf);
420
free_payload = FALSE;
422
header = GST_READ_UINT32_BE (payload);
425
* 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
426
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
427
* | Ident | F |TDT|# pkts.|
428
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
430
* F: Fragment type (0=none, 1=start, 2=cont, 3=end)
431
* TDT: Theora data type (0=theora, 1=config, 2=comment, 3=reserved)
432
* pkts: number of packets.
434
TDT = (header & 0x30) >> 4;
435
if (G_UNLIKELY (TDT == 3))
436
goto ignore_reserved;
438
ident = (header >> 8) & 0xffffff;
439
F = (header & 0xc0) >> 6;
440
packets = (header & 0xf);
443
gboolean do_switch = FALSE;
445
/* we have a raw payload, find the codebook for the ident */
446
if (!rtptheoradepay->config) {
447
/* we don't have an active codebook, find the codebook and
450
} else if (rtptheoradepay->config->ident != ident) {
451
/* codebook changed */
455
if (!gst_rtp_theora_depay_switch_codebook (rtptheoradepay, ident))
464
GST_DEBUG_OBJECT (depayload, "ident: %u, F: %d, TDT: %d, packets: %d", ident,
467
/* fragmented packets, assemble */
473
/* if we start a packet, clear adapter and start assembling. */
474
gst_adapter_clear (rtptheoradepay->adapter);
475
GST_DEBUG_OBJECT (depayload, "start assemble");
476
rtptheoradepay->assembling = TRUE;
479
if (!rtptheoradepay->assembling)
482
/* first assembled packet, reuse 2 bytes to store the length */
483
headerskip = (F == 1 ? 4 : 6);
484
/* skip header and length. */
485
vdata = gst_rtp_buffer_get_payload_subbuffer (buf, headerskip, -1);
487
GST_DEBUG_OBJECT (depayload, "assemble theora packet");
488
gst_adapter_push (rtptheoradepay->adapter, vdata);
490
/* packet is not complete, we are done */
494
/* construct assembled buffer */
495
payload_len = gst_adapter_available (rtptheoradepay->adapter);
496
payload = gst_adapter_take (rtptheoradepay->adapter, payload_len);
498
payload[0] = ((payload_len - 2) >> 8) & 0xff;
499
payload[1] = (payload_len - 2) & 0xff;
503
GST_DEBUG_OBJECT (depayload, "assemble done");
505
/* we not assembling anymore now */
506
rtptheoradepay->assembling = FALSE;
507
gst_adapter_clear (rtptheoradepay->adapter);
509
/* payload now points to a length with that many theora data bytes.
510
* Iterate over the packets and send them out.
513
* 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
514
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
515
* | length | theora data ..
516
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
518
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
519
* | length | next theora packet data ..
520
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
522
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+*
524
timestamp = gst_rtp_buffer_get_timestamp (buf);
526
while (payload_len > 2) {
529
length = GST_READ_UINT16_BE (payload);
533
GST_DEBUG_OBJECT (depayload, "read length %u, avail: %d", length,
536
/* skip packet if something odd happens */
537
if (G_UNLIKELY (length > payload_len))
540
/* create buffer for packet */
541
if (G_UNLIKELY (to_free)) {
542
outbuf = gst_buffer_new ();
543
GST_BUFFER_DATA (outbuf) = payload;
544
GST_BUFFER_MALLOCDATA (outbuf) = to_free;
545
GST_BUFFER_SIZE (outbuf) = length;
548
outbuf = gst_buffer_new_and_alloc (length);
549
memcpy (GST_BUFFER_DATA (outbuf), payload, length);
553
payload_len -= length;
556
/* push with timestamp of the last packet, which is the same timestamp that
557
* should apply to the first assembled packet. */
558
ret = gst_base_rtp_depayload_push_ts (depayload, timestamp, outbuf);
560
ret = gst_base_rtp_depayload_push (depayload, outbuf);
562
if (ret != GST_FLOW_OK)
565
/* make sure we don't set a timestamp on next buffers */
580
GST_ELEMENT_WARNING (rtptheoradepay, STREAM, DECODE,
581
(NULL), ("Packet did not validate"));
586
GST_ELEMENT_ERROR (rtptheoradepay, STREAM, DECODE,
587
(NULL), ("Could not switch codebooks"));
592
GST_ELEMENT_WARNING (rtptheoradepay, STREAM, DECODE,
593
(NULL), ("Packet was too short (%d < 4)", payload_len));
598
GST_WARNING_OBJECT (rtptheoradepay, "reserved TDT ignored");
603
GST_ELEMENT_WARNING (rtptheoradepay, STREAM, DECODE,
604
(NULL), ("Packet contains invalid data"));
610
gst_rtp_theora_depay_set_property (GObject * object, guint prop_id,
611
const GValue * value, GParamSpec * pspec)
613
GstRtpTheoraDepay *rtptheoradepay;
615
rtptheoradepay = GST_RTP_THEORA_DEPAY (object);
619
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
625
gst_rtp_theora_depay_get_property (GObject * object, guint prop_id,
626
GValue * value, GParamSpec * pspec)
628
GstRtpTheoraDepay *rtptheoradepay;
630
rtptheoradepay = GST_RTP_THEORA_DEPAY (object);
634
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
639
static GstStateChangeReturn
640
gst_rtp_theora_depay_change_state (GstElement * element,
641
GstStateChange transition)
643
GstRtpTheoraDepay *rtptheoradepay;
644
GstStateChangeReturn ret;
646
rtptheoradepay = GST_RTP_THEORA_DEPAY (element);
648
switch (transition) {
649
case GST_STATE_CHANGE_NULL_TO_READY:
651
case GST_STATE_CHANGE_READY_TO_PAUSED:
657
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
659
switch (transition) {
660
case GST_STATE_CHANGE_READY_TO_NULL:
669
gst_rtp_theora_depay_plugin_init (GstPlugin * plugin)
671
return gst_element_register (plugin, "rtptheoradepay",
672
GST_RANK_MARGINAL, GST_TYPE_RTP_THEORA_DEPAY);