1 /* GStreamer
2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3 * Copyright (C) <2004> Thomas Vander Stichele <thomas at apestaart dot org>
4 *
5 * dataprotocol.c: Functions implementing the GStreamer Data Protocol
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 */
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
27 #include <gst/gst.h>
28 #include <gst/dataprotocol/dataprotocol.h>
29 #include <glib/gprintf.h> /* g_sprintf */
30 #include <string.h> /* strlen */
31 #include "dp-private.h"
33 /* debug category */
34 GST_DEBUG_CATEGORY (data_protocol_debug);
35 #define GST_CAT_DEFAULT data_protocol_debug
37 /* calculate a CCITT 16 bit CRC check value for a given byte array */
38 /*
39 * this code snippet is adapted from a web page I found
40 * it is identical except for cleanups, and a final XOR with 0xffff
41 * as outlined in the uecp spec
42 *
43 * XMODEM x^16 + x^12 + x^5 + 1
44 */
46 #define POLY 0x1021
47 #define CRC_INIT 0xFFFF
49 /**
50 * gst_dp_crc:
51 *
52 * Calculate a CRC for the given buffer over the given number of bytes.
53 * This is only provided for verification purposes; typical GDP users
54 * will not need this function.
55 *
56 * Returns: a two-byte CRC checksum.
57 */
58 guint16
59 gst_dp_crc (const guint8 * buffer, guint length)
60 {
61 static gboolean initialized = FALSE;
62 static guint16 crc_table[256];
63 guint16 crc_register = CRC_INIT;
64 unsigned long i, j, k;
66 if (!initialized) {
67 for (i = 0; i < 256; i++) {
68 j = i << 8;
69 for (k = 8; k--;) {
70 j = j & 0x8000 ? (j << 1) ^ POLY : j << 1;
71 }
73 crc_table[i] = (guint16) j;
74 }
75 initialized = TRUE;
76 }
78 /* calc CRC */
79 for (; length--;) {
80 crc_register = (guint16) ((crc_register << 8) ^
81 crc_table[((crc_register >> 8) & 0x00ff) ^ *buffer++]);
82 }
83 return (0xffff ^ crc_register);
84 }
86 /* debugging function; dumps byte array values per 8 bytes */
87 /* FIXME: would be nice to merge this with gst_util_dump_mem () */
88 void
89 gst_dp_dump_byte_array (guint8 * array, guint length)
90 {
91 int i;
92 int n = 8; /* number of bytes per line */
93 gchar *line = g_malloc0 (3 * n + 1);
95 GST_LOG ("dumping byte array of length %d", length);
96 for (i = 0; i < length; ++i) {
97 g_sprintf (line + 3 * (i % n), "%02x ", array[i]);
98 if (i % n == (n - 1)) {
99 GST_LOG ("%03d: %s", i - (n - 1), line);
100 }
101 }
102 if (i % n != 0) {
103 GST_LOG ("%03d: %s", (i / n) * n, line);
104 }
105 g_free (line);
106 }
108 /**
109 * gst_dp_init:
110 *
111 * Initialize GStreamer Data Protocol library.
112 *
113 * Should be called before using these functions from source linking
114 * to this source file.
115 */
116 void
117 gst_dp_init (void)
118 {
119 static gboolean _gst_dp_initialized = FALSE;
121 if (_gst_dp_initialized)
122 return;
124 _gst_dp_initialized = TRUE;
126 GST_DEBUG_CATEGORY_INIT (data_protocol_debug, "gdp", 0,
127 "GStreamer Data Protocol");
128 }
130 /*** PUBLIC FUNCTIONS ***/
132 /**
133 * gst_dp_header_payload_length:
134 * @header: the byte header of the packet array
135 *
136 * Get the length of the payload described by @header.
137 *
138 * Returns: the length of the payload this header describes.
139 */
140 guint32
141 gst_dp_header_payload_length (const guint8 * header)
142 {
143 return GST_DP_HEADER_PAYLOAD_LENGTH (header);
144 }
146 /**
147 * gst_dp_header_payload_type:
148 * @header: the byte header of the packet array
149 *
150 * Get the type of the payload described by @header.
151 *
152 * Returns: the #GstDPPayloadType the payload this header describes.
153 */
154 GstDPPayloadType
155 gst_dp_header_payload_type (const guint8 * header)
156 {
157 return GST_DP_HEADER_PAYLOAD_TYPE (header);
158 }
160 /**
161 * gst_dp_header_from_buffer:
162 * @buffer: a #GstBuffer to create a header for
163 * @flags: the #GDPHeaderFlags to create the header with
164 * @length: a guint pointer to store the header length in
165 * @header: a guint8 * pointer to store a newly allocated header byte array in
166 *
167 * Creates a GDP header from the given buffer.
168 *
169 * Returns: %TRUE if the header was successfully created.
170 */
172 gboolean
173 gst_dp_header_from_buffer (const GstBuffer * buffer, GstDPHeaderFlag flags,
174 guint * length, guint8 ** header)
175 {
176 guint8 *h;
177 guint16 crc;
178 guint16 flags_mask;
180 g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
181 g_return_val_if_fail (header, FALSE);
183 *length = GST_DP_HEADER_LENGTH;
184 h = g_malloc0 (GST_DP_HEADER_LENGTH);
186 /* version, flags, type */
187 h[0] = (guint8) GST_DP_VERSION_MAJOR;
188 h[1] = (guint8) GST_DP_VERSION_MINOR;
189 h[2] = (guint8) flags;
190 h[3] = 0; /* padding byte */
191 GST_WRITE_UINT16_BE (h + 4, GST_DP_PAYLOAD_BUFFER);
193 /* buffer properties */
194 GST_WRITE_UINT32_BE (h + 6, GST_BUFFER_SIZE (buffer));
195 GST_WRITE_UINT64_BE (h + 10, GST_BUFFER_TIMESTAMP (buffer));
196 GST_WRITE_UINT64_BE (h + 18, GST_BUFFER_DURATION (buffer));
197 GST_WRITE_UINT64_BE (h + 26, GST_BUFFER_OFFSET (buffer));
198 GST_WRITE_UINT64_BE (h + 34, GST_BUFFER_OFFSET_END (buffer));
200 /* data flags */
201 /* we only copy KEY_UNIT,DELTA_UNIT and IN_CAPS flags */
202 flags_mask = GST_BUFFER_FLAG_PREROLL | GST_BUFFER_FLAG_IN_CAPS |
203 GST_BUFFER_FLAG_DELTA_UNIT;
205 GST_WRITE_UINT16_BE (h + 42, GST_BUFFER_FLAGS (buffer) & flags_mask);
207 /* ABI padding */
208 GST_WRITE_UINT64_BE (h + 44, (guint64) 0);
209 GST_WRITE_UINT32_BE (h + 52, (guint32) 0);
210 GST_WRITE_UINT16_BE (h + 56, (guint16) 0);
212 /* CRC */
213 crc = 0;
214 if (flags & GST_DP_HEADER_FLAG_CRC_HEADER) {
215 /* we don't crc the last four bytes of the header since they are crc's */
216 crc = gst_dp_crc (h, 58);
217 }
218 GST_WRITE_UINT16_BE (h + 58, crc);
220 crc = 0;
221 if (flags & GST_DP_HEADER_FLAG_CRC_PAYLOAD) {
222 crc = gst_dp_crc (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
223 }
224 GST_WRITE_UINT16_BE (h + 60, crc);
226 GST_LOG ("created header from buffer:");
227 gst_dp_dump_byte_array (h, GST_DP_HEADER_LENGTH);
228 *header = h;
229 return TRUE;
230 }
232 /**
233 * gst_dp_packet_from_caps:
234 * @caps: a #GstCaps to create a packet for
235 * @flags: the #GDPHeaderFlags to create the header with
236 * @length: a guint pointer to store the header length in
237 * @header: a guint8 pointer to store a newly allocated header byte array in
238 * @payload: a guint8 pointer to store a newly allocated payload byte array in
239 *
240 * Creates a GDP packet from the given caps.
241 *
242 * Returns: %TRUE if the packet was successfully created.
243 */
244 gboolean
245 gst_dp_packet_from_caps (const GstCaps * caps, GstDPHeaderFlag flags,
246 guint * length, guint8 ** header, guint8 ** payload)
247 {
248 guint8 *h;
249 guint16 crc;
250 guchar *string;
252 /* FIXME: GST_IS_CAPS doesn't work
253 g_return_val_if_fail (GST_IS_CAPS (caps), FALSE); */
254 g_return_val_if_fail (caps, FALSE);
255 g_return_val_if_fail (header, FALSE);
256 g_return_val_if_fail (payload, FALSE);
258 *length = GST_DP_HEADER_LENGTH;
259 h = g_malloc0 (GST_DP_HEADER_LENGTH);
261 string = (guchar *) gst_caps_to_string (caps);
263 /* version, flags, type */
264 h[0] = (guint8) GST_DP_VERSION_MAJOR;
265 h[1] = (guint8) GST_DP_VERSION_MINOR;
266 h[2] = (guint8) flags;
267 h[3] = 0; /* padding bytes */
268 GST_WRITE_UINT16_BE (h + 4, GST_DP_PAYLOAD_CAPS);
270 /* buffer properties */
271 GST_WRITE_UINT32_BE (h + 6, strlen ((gchar *) string) + 1); /* include trailing 0 */
272 GST_WRITE_UINT64_BE (h + 10, (guint64) 0);
273 GST_WRITE_UINT64_BE (h + 18, (guint64) 0);
274 GST_WRITE_UINT64_BE (h + 26, (guint64) 0);
275 GST_WRITE_UINT64_BE (h + 34, (guint64) 0);
277 /* ABI padding */
278 GST_WRITE_UINT64_BE (h + 42, (guint64) 0);
279 GST_WRITE_UINT64_BE (h + 50, (guint64) 0);
281 /* CRC */
282 crc = 0;
283 if (flags & GST_DP_HEADER_FLAG_CRC_HEADER) {
284 crc = gst_dp_crc (h, 58);
285 }
286 GST_WRITE_UINT16_BE (h + 58, crc);
288 crc = 0;
289 if (flags & GST_DP_HEADER_FLAG_CRC_PAYLOAD) {
290 crc = gst_dp_crc (string, strlen ((gchar *) string) + 1);
291 }
292 GST_WRITE_UINT16_BE (h + 60, crc);
294 GST_LOG ("created header from caps:");
295 gst_dp_dump_byte_array (h, GST_DP_HEADER_LENGTH);
296 *header = h;
297 *payload = string;
298 return TRUE;
299 }
301 /**
302 * gst_dp_packet_from_event:
303 * @event: a #GstEvent to create a packet for
304 * @flags: the #GDPHeaderFlags to create the header with
305 * @length: a guint pointer to store the header length in
306 * @header: a guint8 pointer to store a newly allocated header byte array in
307 * @payload: a guint8 pointer to store a newly allocated payload byte array in
308 *
309 * Creates a GDP packet from the given event.
310 *
311 * Returns: %TRUE if the packet was successfully created.
312 */
313 gboolean
314 gst_dp_packet_from_event (const GstEvent * event, GstDPHeaderFlag flags,
315 guint * length, guint8 ** header, guint8 ** payload)
316 {
317 guint8 *h;
318 guint16 crc;
319 guint pl_length; /* length of payload */
321 g_return_val_if_fail (event, FALSE);
322 g_return_val_if_fail (GST_IS_EVENT (event), FALSE);
323 g_return_val_if_fail (header, FALSE);
324 g_return_val_if_fail (payload, FALSE);
326 *length = GST_DP_HEADER_LENGTH;
327 h = g_malloc0 (GST_DP_HEADER_LENGTH);
329 /* first construct payload, since we need the length */
330 switch (GST_EVENT_TYPE (event)) {
331 case GST_EVENT_UNKNOWN:
332 GST_WARNING ("Unknown event, ignoring");
333 *length = 0;
334 g_free (h);
335 return FALSE;
336 case GST_EVENT_EOS:
337 case GST_EVENT_FLUSH_START:
338 case GST_EVENT_FLUSH_STOP:
339 case GST_EVENT_NEWSEGMENT:
340 pl_length = 0;
341 *payload = NULL;
342 break;
343 case GST_EVENT_SEEK:
344 {
345 gdouble rate;
346 GstFormat format;
347 GstSeekFlags flags;
348 GstSeekType cur_type, stop_type;
349 gint64 cur, stop;
351 gst_event_parse_seek ((GstEvent *) event, &rate, &format, &flags,
352 &cur_type, &cur, &stop_type, &stop);
354 pl_length = 4 + 4 + 4 + 8 + 4 + 8;
355 *payload = g_malloc0 (pl_length);
356 /* FIXME write rate */
357 GST_WRITE_UINT32_BE (*payload, (guint32) format);
358 GST_WRITE_UINT32_BE (*payload + 4, (guint32) flags);
359 GST_WRITE_UINT32_BE (*payload + 8, (guint32) cur_type);
360 GST_WRITE_UINT64_BE (*payload + 12, (guint64) cur);
361 GST_WRITE_UINT32_BE (*payload + 20, (guint32) stop_type);
362 GST_WRITE_UINT64_BE (*payload + 24, (guint64) stop);
363 break;
364 }
365 case GST_EVENT_QOS:
366 case GST_EVENT_NAVIGATION:
367 case GST_EVENT_TAG:
368 GST_WARNING ("Unhandled event type %d, ignoring", GST_EVENT_TYPE (event));
369 *length = 0;
370 g_free (h);
371 return FALSE;
372 default:
373 GST_WARNING ("Unknown event type %d, ignoring", GST_EVENT_TYPE (event));
374 *length = 0;
375 g_free (h);
376 return FALSE;
377 }
379 /* version, flags, type */
380 h[0] = (guint8) GST_DP_VERSION_MAJOR;
381 h[1] = (guint8) GST_DP_VERSION_MINOR;
382 h[2] = (guint8) flags;
383 h[3] = 0; /* padding byte */
384 GST_WRITE_UINT16_BE (h + 4,
385 GST_DP_PAYLOAD_EVENT_NONE + GST_EVENT_TYPE (event));
387 /* length */
388 GST_WRITE_UINT32_BE (h + 6, (guint32) pl_length);
389 /* timestamp */
390 GST_WRITE_UINT64_BE (h + 10, GST_EVENT_TIMESTAMP (event));
392 /* ABI padding */
393 GST_WRITE_UINT64_BE (h + 42, (guint64) 0);
394 GST_WRITE_UINT64_BE (h + 50, (guint64) 0);
396 /* CRC */
397 crc = 0;
398 if (flags & GST_DP_HEADER_FLAG_CRC_HEADER) {
399 crc = gst_dp_crc (h, 58);
400 }
401 GST_WRITE_UINT16_BE (h + 58, crc);
403 crc = 0;
404 /* events can have a NULL payload */
405 if (*payload && flags & GST_DP_HEADER_FLAG_CRC_PAYLOAD) {
406 crc = gst_dp_crc (*payload, strlen ((gchar *) * payload) + 1);
407 }
408 GST_WRITE_UINT16_BE (h + 60, crc);
410 GST_LOG ("created header from event:");
411 gst_dp_dump_byte_array (h, GST_DP_HEADER_LENGTH);
412 *header = h;
413 return TRUE;
414 }
417 /**
418 * gst_dp_buffer_from_header:
419 * @header_length: the length of the packet header
420 * @header: the byte array of the packet header
421 *
422 * Creates a newly allocated #GstBuffer from the given header.
423 * The buffer data needs to be copied into it before validating.
424 *
425 * Use this function if you want to pre-allocate a buffer based on the
426 * packet header to read the packet payload in to.
427 *
428 * Returns: A #GstBuffer if the buffer was successfully created, or NULL.
429 */
430 GstBuffer *
431 gst_dp_buffer_from_header (guint header_length, const guint8 * header)
432 {
433 GstBuffer *buffer;
435 g_return_val_if_fail (GST_DP_HEADER_PAYLOAD_TYPE (header) ==
436 GST_DP_PAYLOAD_BUFFER, NULL);
437 buffer =
438 gst_buffer_new_and_alloc ((guint) GST_DP_HEADER_PAYLOAD_LENGTH (header));
439 GST_BUFFER_TIMESTAMP (buffer) = GST_DP_HEADER_TIMESTAMP (header);
440 GST_BUFFER_DURATION (buffer) = GST_DP_HEADER_DURATION (header);
441 GST_BUFFER_OFFSET (buffer) = GST_DP_HEADER_OFFSET (header);
442 GST_BUFFER_OFFSET_END (buffer) = GST_DP_HEADER_OFFSET_END (header);
443 GST_BUFFER_FLAGS (buffer) = GST_DP_HEADER_BUFFER_FLAGS (header);
445 return buffer;
446 }
448 /**
449 * gst_dp_caps_from_packet:
450 * @header_length: the length of the packet header
451 * @header: the byte array of the packet header
452 * @payload: the byte array of the packet payload
453 *
454 * Creates a newly allocated #GstCaps from the given packet.
455 *
456 * Returns: A #GstCaps containing the caps represented in the packet,
457 * or NULL if the packet could not be converted.
458 */
459 GstCaps *
460 gst_dp_caps_from_packet (guint header_length, const guint8 * header,
461 const guint8 * payload)
462 {
463 GstCaps *caps;
464 gchar *string;
466 g_return_val_if_fail (header, NULL);
467 g_return_val_if_fail (payload, NULL);
468 g_return_val_if_fail (GST_DP_HEADER_PAYLOAD_TYPE (header) ==
469 GST_DP_PAYLOAD_CAPS, NULL);
471 string = g_strndup ((gchar *) payload, GST_DP_HEADER_PAYLOAD_LENGTH (header));
472 caps = gst_caps_from_string (string);
473 g_free (string);
474 return caps;
475 }
477 /**
478 * gst_dp_event_from_packet:
479 * @header_length: the length of the packet header
480 * @header: the byte array of the packet header
481 * @payload: the byte array of the packet payload
482 *
483 * Creates a newly allocated #GstEvent from the given packet.
484 *
485 * Returns: A #GstEvent if the event was successfully created,
486 * or NULL if an event could not be read from the payload.
487 */
488 GstEvent *
489 gst_dp_event_from_packet (guint header_length, const guint8 * header,
490 const guint8 * payload)
491 {
492 GstEvent *event = NULL;
493 GstEventType type;
495 g_return_val_if_fail (header, NULL);
496 /* payload can be NULL, e.g. for an EOS event */
498 type = GST_DP_HEADER_PAYLOAD_TYPE (header) - GST_DP_PAYLOAD_EVENT_NONE;
499 switch (type) {
500 case GST_EVENT_UNKNOWN:
501 GST_WARNING ("Unknown event, ignoring");
502 return FALSE;
503 case GST_EVENT_EOS:
504 case GST_EVENT_FLUSH_START:
505 case GST_EVENT_FLUSH_STOP:
506 case GST_EVENT_NEWSEGMENT:
507 event = gst_event_new_custom (type, NULL);
508 GST_EVENT_TIMESTAMP (event) = GST_DP_HEADER_TIMESTAMP (header);
509 break;
510 case GST_EVENT_SEEK:
511 {
512 gdouble rate;
513 GstFormat format;
514 GstSeekFlags flags;
515 GstSeekType cur_type, stop_type;
516 gint64 cur, stop;
518 /* FIXME, read rate */
519 rate = 1.0;
520 format = (GstFormat) GST_READ_UINT32_BE (payload);
521 flags = (GstSeekFlags) GST_READ_UINT32_BE (payload + 4);
522 cur_type = (GstSeekType) GST_READ_UINT32_BE (payload + 8);
523 cur = (gint64) GST_READ_UINT64_BE (payload + 12);
524 stop_type = (GstSeekType) GST_READ_UINT32_BE (payload + 20);
525 stop = (gint64) GST_READ_UINT64_BE (payload + 24);
527 event = gst_event_new_seek (rate, format, flags, cur_type, cur,
528 stop_type, stop);
529 GST_EVENT_TIMESTAMP (event) = GST_DP_HEADER_TIMESTAMP (header);
530 break;
531 }
532 case GST_EVENT_QOS:
533 case GST_EVENT_NAVIGATION:
534 case GST_EVENT_TAG:
535 GST_WARNING ("Unhandled event type %d, ignoring", type);
536 return FALSE;
537 default:
538 GST_WARNING ("Unknown event type %d, ignoring", type);
539 return FALSE;
540 }
542 return event;
543 }
545 /**
546 * gst_dp_validate_header:
547 * @header_length: the length of the packet header
548 * @header: the byte array of the packet header
549 *
550 * Validates the given packet header by checking the CRC checksum.
551 *
552 * Returns: %TRUE if the CRC matches, or no CRC checksum is present.
553 */
554 gboolean
555 gst_dp_validate_header (guint header_length, const guint8 * header)
556 {
557 guint16 crc_read, crc_calculated;
559 if (!(GST_DP_HEADER_FLAGS (header) & GST_DP_HEADER_FLAG_CRC_HEADER))
560 return TRUE;
561 crc_read = GST_DP_HEADER_CRC_HEADER (header);
562 /* don't include the last two crc fields for the crc check */
563 crc_calculated = gst_dp_crc (header, header_length - 4);
564 if (crc_read != crc_calculated) {
565 GST_WARNING ("header crc mismatch: read %02x, calculated %02x", crc_read,
566 crc_calculated);
567 return FALSE;
568 }
569 GST_LOG ("header crc validation: %02x", crc_read);
570 return TRUE;
571 }
573 /**
574 * gst_dp_validate_payload:
575 * @header_length: the length of the packet header
576 * @header: the byte array of the packet header
577 * @payload: the byte array of the packet payload
578 *
579 * Validates the given packet payload using the given packet header
580 * by checking the CRC checksum.
581 *
582 * Returns: %TRUE if the CRC matches, or no CRC checksum is present.
583 */
584 gboolean
585 gst_dp_validate_payload (guint header_length, const guint8 * header,
586 const guint8 * payload)
587 {
588 guint16 crc_read, crc_calculated;
590 if (!(GST_DP_HEADER_FLAGS (header) & GST_DP_HEADER_FLAG_CRC_PAYLOAD))
591 return TRUE;
592 crc_read = GST_DP_HEADER_CRC_PAYLOAD (header);
593 crc_calculated = gst_dp_crc (payload, GST_DP_HEADER_PAYLOAD_LENGTH (header));
594 if (crc_read != crc_calculated) {
595 GST_WARNING ("payload crc mismatch: read %02x, calculated %02x", crc_read,
596 crc_calculated);
597 return FALSE;
598 }
599 GST_LOG ("payload crc validation: %02x", crc_read);
600 return TRUE;
601 }
603 /**
604 * gst_dp_validate_packet:
605 * @header_length: the length of the packet header
606 * @header: the byte array of the packet header
607 * @payload: the byte array of the packet payload
608 *
609 * Validates the given packet by checking version information and checksums.
610 *
611 * Returns: %TRUE if the packet validates.
612 */
613 gboolean
614 gst_dp_validate_packet (guint header_length, const guint8 * header,
615 const guint8 * payload)
616 {
617 if (!gst_dp_validate_header (header_length, header))
618 return FALSE;
619 if (!gst_dp_validate_payload (header_length, header, payload))
620 return FALSE;
622 return TRUE;
623 }