7138be2c26edcd31bc18b5e7b53b3ffc23b5c44a
1 /* GStreamer RTP ASF depayloader
2 * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
3 * 2009 Wim Taymans <wim.taymans@gmail.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 */
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
25 #include "gstrtpasfdepay.h"
26 #include <gst/rtp/gstrtpbuffer.h>
28 #include <string.h>
29 #include <stdlib.h>
31 GST_DEBUG_CATEGORY_STATIC (rtpasfdepayload_debug);
32 #define GST_CAT_DEFAULT rtpasfdepayload_debug
34 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
35 GST_PAD_SRC,
36 GST_PAD_ALWAYS,
37 GST_STATIC_CAPS ("video/x-ms-asf")
38 );
40 /* Other parameters: config, maxps */
41 #define SINK_CAPS \
42 "application/x-rtp, " \
43 "media = (string) { \"application\", \"video\", \"audio\" }, " \
44 "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " \
45 "clock-rate = (int) [1, MAX ], " \
46 "encoding-name = (string) \"X-ASF-PF\""
48 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
49 GST_PAD_SINK,
50 GST_PAD_ALWAYS,
51 GST_STATIC_CAPS (SINK_CAPS)
52 );
54 GST_BOILERPLATE (GstRtpAsfDepay, gst_rtp_asf_depay, GstBaseRTPDepayload,
55 GST_TYPE_BASE_RTP_DEPAYLOAD);
57 static void gst_rtp_asf_depay_finalize (GObject * object);
59 static GstStateChangeReturn gst_rtp_asf_depay_change_state (GstElement *
60 element, GstStateChange transition);
62 static gboolean gst_rtp_asf_depay_setcaps (GstBaseRTPDepayload * depay,
63 GstCaps * caps);
64 static GstBuffer *gst_rtp_asf_depay_process (GstBaseRTPDepayload * basedepay,
65 GstBuffer * buf);
67 static void
68 gst_rtp_asf_depay_base_init (gpointer klass)
69 {
70 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
72 gst_element_class_add_pad_template (element_class,
73 gst_static_pad_template_get (&src_factory));
74 gst_element_class_add_pad_template (element_class,
75 gst_static_pad_template_get (&sink_factory));
77 gst_element_class_set_details_simple (element_class,
78 "RTP ASF packet depayloader", "Codec/Depayloader/Network",
79 "Extracts ASF streams from RTP",
80 "Tim-Philipp Müller <tim centricular net>, "
81 "Wim Taymans <wim.taymans@gmail.com>");
82 }
84 static void
85 gst_rtp_asf_depay_class_init (GstRtpAsfDepayClass * klass)
86 {
87 GObjectClass *gobject_class;
88 GstElementClass *gstelement_class;
89 GstBaseRTPDepayloadClass *gstbasertpdepayload_class;
91 gobject_class = (GObjectClass *) klass;
92 gstelement_class = (GstElementClass *) klass;
93 gstbasertpdepayload_class = (GstBaseRTPDepayloadClass *) klass;
95 gobject_class->finalize = gst_rtp_asf_depay_finalize;
97 gstelement_class->change_state =
98 GST_DEBUG_FUNCPTR (gst_rtp_asf_depay_change_state);
100 gstbasertpdepayload_class->set_caps =
101 GST_DEBUG_FUNCPTR (gst_rtp_asf_depay_setcaps);
102 gstbasertpdepayload_class->process =
103 GST_DEBUG_FUNCPTR (gst_rtp_asf_depay_process);
105 GST_DEBUG_CATEGORY_INIT (rtpasfdepayload_debug, "rtpasfdepayload", 0,
106 "RTP asf depayloader element");
107 }
109 static void
110 gst_rtp_asf_depay_init (GstRtpAsfDepay * depay, GstRtpAsfDepayClass * klass)
111 {
112 depay->adapter = gst_adapter_new ();
113 }
115 static void
116 gst_rtp_asf_depay_finalize (GObject * object)
117 {
118 GstRtpAsfDepay *depay;
120 depay = GST_RTP_ASF_DEPAY (object);
122 g_object_unref (depay->adapter);
124 G_OBJECT_CLASS (parent_class)->finalize (object);
125 }
127 static const guint8 asf_marker[16] = { 0x30, 0x26, 0xb2, 0x75, 0x8e, 0x66,
128 0xcf, 0x11, 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c
129 };
131 static gboolean
132 gst_rtp_asf_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps)
133 {
134 GstRtpAsfDepay *depay;
135 GstStructure *s;
136 const gchar *config_str, *ps_string;
137 GstBuffer *buf;
138 GstCaps *src_caps;
139 guint8 *headers;
140 gsize headers_len;
141 gint clock_rate;
143 depay = GST_RTP_ASF_DEPAY (depayload);
145 s = gst_caps_get_structure (caps, 0);
147 if (!gst_structure_get_int (s, "clock-rate", &clock_rate) || clock_rate < 0)
148 clock_rate = 1000;
149 depayload->clock_rate = clock_rate;
151 /* config contains the asf headers in base64 coding */
152 config_str = gst_structure_get_string (s, "config");
153 if (config_str == NULL || *config_str == '\0')
154 goto no_config;
156 ps_string = gst_structure_get_string (s, "maxps");
157 if (ps_string == NULL || *ps_string == '\0')
158 goto no_packetsize;
160 if (depay->packet_size) {
161 /* header sent again following seek;
162 * discard to avoid confusing upstream */
163 if (depay->packet_size == atoi (ps_string)) {
164 goto duplicate_header;
165 } else {
166 /* since we should fiddle with downstream state to handle this */
167 goto refuse_renegotiation;
168 }
169 } else
170 depay->packet_size = atoi (ps_string);
171 if (depay->packet_size <= 16)
172 goto invalid_packetsize;
174 headers = (guint8 *) g_base64_decode (config_str, &headers_len);
176 if (headers == NULL || headers_len < 16
177 || memcmp (headers, asf_marker, 16) != 0)
178 goto invalid_headers;
180 src_caps = gst_caps_new_simple ("video/x-ms-asf", NULL);
181 gst_pad_set_caps (depayload->srcpad, src_caps);
183 buf = gst_buffer_new ();
184 GST_BUFFER_DATA (buf) = headers;
185 GST_BUFFER_MALLOCDATA (buf) = headers;
186 GST_BUFFER_SIZE (buf) = headers_len;
187 gst_buffer_set_caps (buf, src_caps);
188 gst_caps_unref (src_caps);
190 gst_base_rtp_depayload_push (depayload, buf);
192 return TRUE;
194 /* ERRORS */
195 no_config:
196 {
197 GST_WARNING_OBJECT (depay, "caps without 'config' field with asf headers");
198 return FALSE;
199 }
200 no_packetsize:
201 {
202 GST_WARNING_OBJECT (depay, "caps without 'maxps' (packet size) field");
203 return FALSE;
204 }
205 invalid_packetsize:
206 {
207 GST_WARNING_OBJECT (depay, "packet size %u invalid", depay->packet_size);
208 return FALSE;
209 }
210 invalid_headers:
211 {
212 GST_WARNING_OBJECT (depay, "headers don't look like valid ASF headers");
213 g_free (headers);
214 return FALSE;
215 }
216 duplicate_header:
217 {
218 GST_DEBUG_OBJECT (depayload, "discarding duplicate header");
219 return TRUE;
220 }
221 refuse_renegotiation:
222 {
223 GST_WARNING_OBJECT (depayload, "cannot renegotiate to different header");
224 return FALSE;
225 }
226 }
228 static gint
229 field_size (guint8 field)
230 {
231 switch (field) {
232 /* DWORD - 32 bits */
233 case 3:
234 return 4;
236 /* WORD - 16 bits */
237 case 2:
238 return 2;
240 /* BYTE - 8 bits */
241 case 1:
242 return 1;
244 /* non-exitent */
245 case 0:
246 default:
247 return 0;
248 }
249 }
251 /*
252 * Set the padding field to te correct value as the spec
253 * says it should be se to 0 in the rtp packets
254 */
255 static void
256 gst_rtp_asf_depay_set_padding (GstRtpAsfDepay * depayload,
257 GstBuffer * buf, guint32 padding)
258 {
259 guint8 *data = GST_BUFFER_DATA (buf);
260 gint offset = 0;
261 guint8 aux;
262 guint8 seq_type;
263 guint8 pad_type;
264 guint8 pkt_type;
266 aux = data[offset++];
267 if (aux & 0x80) {
268 guint8 err_len = 0;
269 if (aux & 0x60) {
270 GST_WARNING_OBJECT (depayload, "Error correction length type should be "
271 "set to 0");
272 /* this packet doesn't follow the spec */
273 return;
274 }
275 err_len = aux & 0x0F;
276 offset += err_len;
278 aux = data[offset++];
279 }
280 seq_type = (aux >> 1) & 0x3;
281 pad_type = (aux >> 3) & 0x3;
282 pkt_type = (aux >> 5) & 0x3;
284 offset += 1; /* skip property flags */
285 offset += field_size (pkt_type); /* skip packet length */
286 offset += field_size (seq_type); /* skip sequence field */
288 /* write padding */
289 switch (pad_type) {
290 /* DWORD */
291 case 3:
292 GST_WRITE_UINT32_LE (&(data[offset]), padding);
293 break;
295 /* WORD */
296 case 2:
297 GST_WRITE_UINT16_LE (&(data[offset]), padding);
298 break;
300 /* BYTE */
301 case 1:
302 data[offset] = (guint8) padding;
303 break;
305 /* non-existent */
306 case 0:
307 default:
308 break;
309 }
310 }
312 /* Docs: 'RTSP Protocol PDF' document from http://sdp.ppona.com/ (page 8) */
314 static GstBuffer *
315 gst_rtp_asf_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
316 {
317 GstRtpAsfDepay *depay;
318 const guint8 *payload;
319 GstBuffer *outbuf;
320 gboolean S, L, R, D, I;
321 guint payload_len, hdr_len, offset;
322 guint len_offs;
323 GstClockTime timestamp;
325 depay = GST_RTP_ASF_DEPAY (depayload);
327 /* flush remaining data on discont */
328 if (GST_BUFFER_IS_DISCONT (buf)) {
329 GST_LOG_OBJECT (depay, "got DISCONT");
330 gst_adapter_clear (depay->adapter);
331 depay->discont = TRUE;
332 }
334 timestamp = GST_BUFFER_TIMESTAMP (buf);
336 payload_len = gst_rtp_buffer_get_payload_len (buf);
337 payload = gst_rtp_buffer_get_payload (buf);
338 offset = 0;
340 GST_LOG_OBJECT (depay, "got payload len of %u", payload_len);
342 do {
343 guint packet_len;
345 /* packet header is at least 4 bytes */
346 if (payload_len < 4)
347 goto too_small;
349 /* 1 2 3
350 * 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
351 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
352 * |S|L|R|D|I|RES | Length/Offset |
353 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
354 * | Relative Timestamp (optional) |
355 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
356 * | Duration (optional) |
357 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
358 * | LocationId (optional) |
359 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
360 *
361 * S: packet contains a keyframe.
362 * L: If 1, Length/Offset contains length, else contains the byte offset
363 * of the fragment's first byte counted from the beginning of the
364 * complete ASF data packet.
365 * R: relative timestamp present
366 * D: duration present
367 * I: locationid present
368 */
370 S = ((payload[0] & 0x80) != 0);
371 L = ((payload[0] & 0x40) != 0);
372 R = ((payload[0] & 0x20) != 0);
373 D = ((payload[0] & 0x10) != 0);
374 I = ((payload[0] & 0x08) != 0);
376 hdr_len = 4;
378 len_offs = (payload[1] << 16) | (payload[2] << 8) | payload[3];
380 if (R) {
381 GST_DEBUG ("Relative timestamp field present : %u",
382 GST_READ_UINT32_BE (payload + hdr_len));
383 hdr_len += 4;
384 }
385 if (D) {
386 GST_DEBUG ("Duration field present : %u",
387 GST_READ_UINT32_BE (payload + hdr_len));
388 hdr_len += 4;
389 }
390 if (I) {
391 GST_DEBUG ("LocationId field present : %u",
392 GST_READ_UINT32_BE (payload + hdr_len));
393 hdr_len += 4;
394 }
396 GST_LOG_OBJECT (depay, "S %d, L %d, R %d, D %d, I %d", S, L, R, D, I);
397 GST_LOG_OBJECT (depay, "payload_len:%d, hdr_len:%d, len_offs:%d",
398 payload_len, hdr_len, len_offs);
400 if (payload_len < hdr_len)
401 goto too_small;
403 /* skip headers */
404 payload_len -= hdr_len;
405 payload += hdr_len;
406 offset += hdr_len;
408 if (L) {
409 /* L bit set, len contains the length of the packet */
410 packet_len = len_offs;
411 } else {
412 /* else it contains an offset which we don't handle yet */
413 GST_LOG_OBJECT (depay, "We have a fragmented packet");
414 packet_len = payload_len;
415 }
417 if (packet_len > payload_len)
418 packet_len = payload_len;
420 GST_LOG_OBJECT (depay, "packet len %u, payload len %u, packet_size:%u",
421 packet_len, payload_len, depay->packet_size);
423 if (!L) {
424 guint available;
425 GstBuffer *sub;
427 /* Fragmented packet handling */
428 outbuf = NULL;
430 if (len_offs == (available = gst_adapter_available (depay->adapter))) {
431 /* fragment aligns with what we have, add it */
432 GST_LOG_OBJECT (depay, "collecting fragment");
433 sub = gst_rtp_buffer_get_payload_subbuffer (buf, offset, packet_len);
434 gst_adapter_push (depay->adapter, sub);
435 /* RTP marker bit M is set if this is last fragment */
436 if (gst_rtp_buffer_get_marker (buf)) {
437 GST_LOG_OBJECT (depay, "last fragment, assembling packet");
438 outbuf =
439 gst_adapter_take_buffer (depay->adapter, available + packet_len);
440 }
441 } else {
442 if (available) {
443 GST_WARNING_OBJECT (depay, "Offset doesn't match previous data?!");
444 GST_DEBUG_OBJECT (depay, "clearing for re-sync");
445 gst_adapter_clear (depay->adapter);
446 } else
447 GST_DEBUG_OBJECT (depay, "waiting for start of packet");
448 }
449 } else {
450 GST_LOG_OBJECT (depay, "collecting packet");
451 outbuf = gst_rtp_buffer_get_payload_subbuffer (buf, offset, packet_len);
452 }
454 /* If we haven't completed a full ASF packet, return */
455 if (!outbuf)
456 return NULL;
458 /* we need to pad with zeroes to packet_size if it's smaller */
459 if (GST_BUFFER_SIZE (outbuf) < depay->packet_size) {
460 GstBuffer *tmp;
461 gint plen = GST_BUFFER_SIZE (outbuf);
463 GST_LOG_OBJECT (depay, "padding buffer size %d to packet size %d",
464 plen, depay->packet_size);
465 tmp = gst_buffer_new_and_alloc (depay->packet_size);
466 memcpy (GST_BUFFER_DATA (tmp), GST_BUFFER_DATA (outbuf), plen);
467 gst_buffer_copy_metadata (tmp, outbuf, GST_BUFFER_COPY_ALL);
468 gst_buffer_unref (outbuf);
469 outbuf = tmp;
470 memset (GST_BUFFER_DATA (outbuf) + plen, 0, depay->packet_size - plen);
471 gst_rtp_asf_depay_set_padding (depay, outbuf, depay->packet_size - plen);
472 }
474 gst_buffer_set_caps (outbuf, GST_PAD_CAPS (depayload->srcpad));
476 if (!S)
477 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
479 if (depay->discont) {
480 GST_LOG_OBJECT (depay, "setting DISCONT");
481 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
482 depay->discont = FALSE;
483 }
485 GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
487 gst_base_rtp_depayload_push (depayload, outbuf);
489 /* only apply the timestamp to the first buffer of this packet */
490 timestamp = -1;
492 /* skip packet data */
493 payload += packet_len;
494 offset += packet_len;
495 payload_len -= packet_len;
496 } while (payload_len > 0);
498 return NULL;
500 /* ERRORS */
501 too_small:
502 {
503 GST_WARNING_OBJECT (depayload, "Payload too small, expected at least 4 "
504 "bytes for header, but got only %d bytes", payload_len);
505 return NULL;
506 }
507 }
509 static GstStateChangeReturn
510 gst_rtp_asf_depay_change_state (GstElement * element, GstStateChange trans)
511 {
512 GstStateChangeReturn ret;
513 GstRtpAsfDepay *depay;
515 depay = GST_RTP_ASF_DEPAY (element);
517 switch (trans) {
518 case GST_STATE_CHANGE_READY_TO_PAUSED:
519 gst_adapter_clear (depay->adapter);
520 depay->discont = TRUE;
521 break;
522 default:
523 break;
524 }
526 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, trans);
528 switch (trans) {
529 case GST_STATE_CHANGE_PAUSED_TO_READY:
530 gst_adapter_clear (depay->adapter);
531 break;
532 default:
533 break;
534 }
536 return ret;
537 }