1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) 2004,2006 Thomas Vander Stichele <thomas at apestaart dot org>
4  * Copyright (C) 2014 Tim-Philipp Müller <tim centricular com>
5  *
6  * dataprotocol.c: Functions implementing the GStreamer Data Protocol
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23 
24 /**
25  * SECTION:gstdataprotocol
26  * @title: GstDataProtocol
27  * @short_description: Serialization of caps, buffers and events.
28  * @see_also: #GstCaps, #GstEvent, #GstBuffer
29  *
30  * This helper library provides serialization of GstBuffer, GstCaps and
31  * GstEvent structures.
32  *
33  * This serialization is useful when GStreamer needs to interface with
34  * the outside world to transport data between distinct GStreamer pipelines.
35  * The connections with the outside world generally don't have mechanisms
36  * to transport properties of these structures.
37  *
38  * For example, transporting buffers across named pipes or network connections
39  * doesn't maintain the buffer size and separation.
40  *
41  * This data protocol assumes a reliable connection-oriented transport, such as
42  * TCP, a pipe, or a file.  The protocol does not serialize the caps for
43  * each buffer; instead, it transport the caps only when they change in the
44  * stream.  This implies that there will always be a caps packet before any
45  * buffer packets.
46  *
47  * The versioning of the protocol is independent of GStreamer's version.
48  * The major number gets incremented, and the minor reset, for incompatible
49  * changes.  The minor number gets incremented for compatible changes that
50  * allow clients who do not completely understand the newer protocol version
51  * to still decode what they do understand.
52  *
53  * Version 0.2 serializes only a small subset of all events, with a custom
54  * payload for each type.  Also, all GDP streams start with the initial caps
55  * packet.
56  *
57  * Version 1.0 serializes all events by taking the string representation of
58  * the event as the payload.  In addition, GDP streams can now start with
59  * events as well, as required by the new data stream model in GStreamer 0.10.
60  *
61  * Converting buffers, caps and events to GDP buffers is done using the
62  * appropriate functions.
63  *
64  * For reference, this image shows the byte layout of the GDP header:
65  *
66  * <inlinegraphic format="PNG" fileref="gdp-header.png"></inlinegraphic>
67  */
68 
69 #ifdef HAVE_CONFIG_H
70 #include "config.h"
71 #endif
72 
73 #include <gst/gst.h>
74 #include "dataprotocol.h"
75 #include <glib/gprintf.h>       /* g_sprintf */
76 #include <string.h>             /* strlen */
77 #include "dp-private.h"
78 
79 /* debug category */
80 GST_DEBUG_CATEGORY_STATIC (data_protocol_debug);
81 #ifndef GST_CAT_DEFAULT
82 #define GST_CAT_DEFAULT data_protocol_debug
83 #endif
84 
85 /* The version of the GDP protocol being used */
86 typedef enum
87 {
88   GST_DP_VERSION_0_2 = 1,
89   GST_DP_VERSION_1_0,
90 } GstDPVersion;
91 
92 /* helper macros */
93 
94 /* write first 6 bytes of header */
95 #define GST_DP_INIT_HEADER(h, version, flags, type)		\
96 G_STMT_START {							\
97   gint maj = 0, min = 0;					\
98   switch (version) {						\
99     case GST_DP_VERSION_0_2: maj = 0; min = 2; break;		\
100     case GST_DP_VERSION_1_0: maj = 1; min = 0; break;		\
101   }								\
102   h[0] = (guint8) maj;						\
103   h[1] = (guint8) min;						\
104   h[2] = (guint8) flags;					\
105   h[3] = 0; /* padding byte */					\
106   GST_WRITE_UINT16_BE (h + 4, type);				\
107 } G_STMT_END
108 
109 #define GST_DP_SET_CRC(h, flags, payload, length);		\
110 G_STMT_START {							\
111   guint16 crc = 0;						\
112   if (flags & GST_DP_HEADER_FLAG_CRC_HEADER)			\
113     /* we don't crc the last four bytes since they are crc's */ \
114     crc = gst_dp_crc (h, 58);					\
115   GST_WRITE_UINT16_BE (h + 58, crc);				\
116 								\
117   crc = 0;							\
118   if (length && (flags & GST_DP_HEADER_FLAG_CRC_PAYLOAD))	\
119     crc = gst_dp_crc (payload, length);				\
120   GST_WRITE_UINT16_BE (h + 60, crc);				\
121 } G_STMT_END
122 
123 /* calculate a CCITT 16 bit CRC check value for a given byte array */
124 /*
125  * this code snippet is adapted from a web page I found
126  * it is identical except for cleanups, and a final XOR with 0xffff
127  * as outlined in the uecp spec
128  *
129  * XMODEM    x^16 + x^12 + x^5 + 1
130  */
131 
132 #define POLY       0x1021
133 #define CRC_INIT   0xFFFF
134 
135 static guint16 gst_dp_crc (const guint8 * buffer, guint length);
136 static guint16 gst_dp_crc_from_memory_maps (const GstMapInfo * maps,
137     guint n_maps);
138 
139 /* payloading functions */
140 
141 GstBuffer *
gst_dp_payload_buffer(GstBuffer * buffer,GstDPHeaderFlag flags)142 gst_dp_payload_buffer (GstBuffer * buffer, GstDPHeaderFlag flags)
143 {
144   GstBuffer *ret_buf;
145   GstMapInfo map;
146   GstMemory *mem;
147   guint8 *h;
148   guint16 flags_mask;
149   guint16 header_crc = 0, crc = 0;
150   gsize buffer_size;
151 
152   mem = gst_allocator_alloc (NULL, GST_DP_HEADER_LENGTH, NULL);
153   gst_memory_map (mem, &map, GST_MAP_READWRITE);
154   h = memset (map.data, 0, map.size);
155 
156   /* version, flags, type */
157   GST_DP_INIT_HEADER (h, GST_DP_VERSION_1_0, flags, GST_DP_PAYLOAD_BUFFER);
158 
159   if ((flags & GST_DP_HEADER_FLAG_CRC_PAYLOAD)) {
160     GstMapInfo *maps;
161     guint n_maps, i;
162 
163     buffer_size = 0;
164 
165     n_maps = gst_buffer_n_memory (buffer);
166     if (n_maps > 0) {
167       maps = g_newa (GstMapInfo, n_maps);
168 
169       for (i = 0; i < n_maps; ++i) {
170         GstMemory *mem;
171 
172         mem = gst_buffer_peek_memory (buffer, i);
173         gst_memory_map (mem, &maps[i], GST_MAP_READ);
174         buffer_size += maps[i].size;
175       }
176 
177       crc = gst_dp_crc_from_memory_maps (maps, n_maps);
178 
179       for (i = 0; i < n_maps; ++i)
180         gst_memory_unmap (maps[i].memory, &maps[i]);
181     }
182   } else {
183     buffer_size = gst_buffer_get_size (buffer);
184   }
185 
186   /* buffer properties */
187   GST_WRITE_UINT32_BE (h + 6, buffer_size);
188   GST_WRITE_UINT64_BE (h + 10, GST_BUFFER_TIMESTAMP (buffer));
189   GST_WRITE_UINT64_BE (h + 18, GST_BUFFER_DURATION (buffer));
190   GST_WRITE_UINT64_BE (h + 26, GST_BUFFER_OFFSET (buffer));
191   GST_WRITE_UINT64_BE (h + 34, GST_BUFFER_OFFSET_END (buffer));
192 
193   /* data flags; eats two bytes from the ABI area */
194   /* we copy everything but the read-only flags */
195   flags_mask = GST_BUFFER_FLAG_LIVE | GST_BUFFER_FLAG_DISCONT |
196       GST_BUFFER_FLAG_HEADER | GST_BUFFER_FLAG_GAP | GST_BUFFER_FLAG_DELTA_UNIT;
197 
198   GST_WRITE_UINT16_BE (h + 42, GST_BUFFER_FLAGS (buffer) & flags_mask);
199 
200   /* from gstreamer 1.x, buffers also have the DTS */
201   GST_WRITE_UINT64_BE (h + 44, GST_BUFFER_DTS (buffer));
202 
203   /* header CRC */
204   if ((flags & GST_DP_HEADER_FLAG_CRC_HEADER))
205     /* we don't crc the last four bytes since they are crc's */
206     header_crc = gst_dp_crc (h, 58);
207   else
208     header_crc = 0;
209 
210   GST_WRITE_UINT16_BE (h + 58, header_crc);
211 
212   /* payload CRC */
213   GST_WRITE_UINT16_BE (h + 60, crc);
214 
215   GST_MEMDUMP ("payload header for buffer", h, GST_DP_HEADER_LENGTH);
216   gst_memory_unmap (mem, &map);
217 
218   ret_buf = gst_buffer_new ();
219 
220   /* header */
221   gst_buffer_append_memory (ret_buf, mem);
222 
223   /* buffer data */
224   return gst_buffer_append (ret_buf, gst_buffer_ref (buffer));
225 }
226 
227 GstBuffer *
gst_dp_payload_caps(const GstCaps * caps,GstDPHeaderFlag flags)228 gst_dp_payload_caps (const GstCaps * caps, GstDPHeaderFlag flags)
229 {
230   GstBuffer *buf;
231   GstMapInfo map;
232   GstMemory *mem;
233   guint8 *h;
234   guchar *string;
235   guint payload_length;
236 
237   g_assert (GST_IS_CAPS (caps));
238 
239   buf = gst_buffer_new ();
240 
241   mem = gst_allocator_alloc (NULL, GST_DP_HEADER_LENGTH, NULL);
242   gst_memory_map (mem, &map, GST_MAP_READWRITE);
243   h = memset (map.data, 0, map.size);
244 
245   string = (guchar *) gst_caps_to_string (caps);
246   payload_length = strlen ((gchar *) string) + 1;       /* include trailing 0 */
247 
248   /* version, flags, type */
249   GST_DP_INIT_HEADER (h, GST_DP_VERSION_1_0, flags, GST_DP_PAYLOAD_CAPS);
250 
251   /* buffer properties */
252   GST_WRITE_UINT32_BE (h + 6, payload_length);
253   GST_WRITE_UINT64_BE (h + 10, (guint64) 0);
254   GST_WRITE_UINT64_BE (h + 18, (guint64) 0);
255   GST_WRITE_UINT64_BE (h + 26, (guint64) 0);
256   GST_WRITE_UINT64_BE (h + 34, (guint64) 0);
257 
258   GST_DP_SET_CRC (h, flags, string, payload_length);
259 
260   GST_MEMDUMP ("payload header for caps", h, GST_DP_HEADER_LENGTH);
261   gst_memory_unmap (mem, &map);
262 
263   /* header */
264   gst_buffer_append_memory (buf, mem);
265 
266   /* caps string */
267   gst_buffer_append_memory (buf,
268       gst_memory_new_wrapped (0, string, payload_length, 0, payload_length,
269           string, g_free));
270 
271   return buf;
272 }
273 
274 GstBuffer *
gst_dp_payload_event(const GstEvent * event,GstDPHeaderFlag flags)275 gst_dp_payload_event (const GstEvent * event, GstDPHeaderFlag flags)
276 {
277   GstBuffer *buf;
278   GstMapInfo map;
279   GstMemory *mem;
280   guint8 *h;
281   guint32 pl_length;            /* length of payload */
282   guchar *string = NULL;
283   const GstStructure *structure;
284 
285   g_assert (GST_IS_EVENT (event));
286 
287   buf = gst_buffer_new ();
288 
289   mem = gst_allocator_alloc (NULL, GST_DP_HEADER_LENGTH, NULL);
290   gst_memory_map (mem, &map, GST_MAP_READWRITE);
291   h = memset (map.data, 0, map.size);
292 
293   structure = gst_event_get_structure ((GstEvent *) event);
294   if (structure) {
295     string = (guchar *) gst_structure_to_string (structure);
296     GST_LOG ("event %p has structure, string %s", event, string);
297     pl_length = strlen ((gchar *) string) + 1;  /* include trailing 0 */
298   } else {
299     GST_LOG ("event %p has no structure", event);
300     pl_length = 0;
301   }
302 
303   /* version, flags, type */
304   GST_DP_INIT_HEADER (h, GST_DP_VERSION_1_0, flags,
305       GST_DP_PAYLOAD_EVENT_NONE + GST_EVENT_TYPE (event));
306 
307   /* length */
308   GST_WRITE_UINT32_BE (h + 6, pl_length);
309   /* timestamp */
310   /* NOTE: timestamp field will be removed from GstEvent in 2.0 API */
311   GST_WRITE_UINT64_BE (h + 10, GST_CLOCK_TIME_NONE);
312 
313   GST_DP_SET_CRC (h, flags, string, pl_length);
314 
315   GST_MEMDUMP ("payload header for event", h, GST_DP_HEADER_LENGTH);
316   gst_memory_unmap (mem, &map);
317 
318   /* header */
319   gst_buffer_append_memory (buf, mem);
320 
321   /* event string */
322   if (pl_length > 0) {
323     gst_buffer_append_memory (buf,
324         gst_memory_new_wrapped (0, string, pl_length, 0, pl_length,
325             string, g_free));
326   }
327 
328   return buf;
329 }
330 
331 /*** PUBLIC FUNCTIONS ***/
332 
333 static const guint16 gst_dp_crc_table[256] = {
334   0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
335   0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
336   0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
337   0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
338   0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
339   0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
340   0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
341   0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
342   0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
343   0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
344   0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
345   0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
346   0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
347   0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
348   0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
349   0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
350   0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
351   0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
352   0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
353   0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
354   0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
355   0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
356   0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
357   0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
358   0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
359   0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
360   0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
361   0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
362   0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
363   0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
364   0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
365   0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
366 };
367 
368 /**
369  * gst_dp_crc:
370  * @buffer: array of bytes
371  * @length: the length of @buffer
372  *
373  * Calculate a CRC for the given buffer over the given number of bytes.
374  * This is only provided for verification purposes; typical GDP users
375  * will not need this function.
376  *
377  * Returns: a two-byte CRC checksum.
378  */
379 static guint16
gst_dp_crc(const guint8 * buffer,guint length)380 gst_dp_crc (const guint8 * buffer, guint length)
381 {
382   guint16 crc_register = CRC_INIT;
383 
384   if (length == 0)
385     return 0;
386 
387   g_assert (buffer != NULL);
388 
389   /* calc CRC */
390   for (; length--;) {
391     crc_register = (guint16) ((crc_register << 8) ^
392         gst_dp_crc_table[((crc_register >> 8) & 0x00ff) ^ *buffer++]);
393   }
394   return (0xffff ^ crc_register);
395 }
396 
397 static guint16
gst_dp_crc_from_memory_maps(const GstMapInfo * maps,guint n_maps)398 gst_dp_crc_from_memory_maps (const GstMapInfo * maps, guint n_maps)
399 {
400   guint16 crc_register = CRC_INIT;
401   gsize total_length = 0;
402 
403   if (n_maps == 0)
404     return 0;
405 
406   g_assert (maps != NULL);
407 
408   /* calc CRC */
409   while (n_maps > 0) {
410     guint8 *buffer = maps->data;
411     gsize length = maps->size;
412 
413     total_length += length;
414 
415     while (length-- > 0) {
416       crc_register = (guint16) ((crc_register << 8) ^
417           gst_dp_crc_table[((crc_register >> 8) & 0x00ff) ^ *buffer++]);
418     }
419     --n_maps;
420     ++maps;
421   }
422 
423   if (G_UNLIKELY (total_length == 0))
424     return 0;
425 
426   return (0xffff ^ crc_register);
427 }
428 
429 /**
430  * gst_dp_init:
431  *
432  * Initialize GStreamer Data Protocol library.
433  *
434  * Should be called before using these functions from source linking
435  * to this source file.
436  */
437 void
gst_dp_init(void)438 gst_dp_init (void)
439 {
440   GST_DEBUG_CATEGORY_INIT (data_protocol_debug, "gdp", 0,
441       "GStreamer Data Protocol");
442 }
443 
444 /**
445  * gst_dp_header_payload_length:
446  * @header: the byte header of the packet array
447  *
448  * Get the length of the payload described by @header.
449  *
450  * Returns: the length of the payload this header describes.
451  */
452 guint32
gst_dp_header_payload_length(const guint8 * header)453 gst_dp_header_payload_length (const guint8 * header)
454 {
455   g_return_val_if_fail (header != NULL, 0);
456 
457   return GST_DP_HEADER_PAYLOAD_LENGTH (header);
458 }
459 
460 /**
461  * gst_dp_header_payload_type:
462  * @header: the byte header of the packet array
463  *
464  * Get the type of the payload described by @header.
465  *
466  * Returns: the #GstDPPayloadType the payload this header describes.
467  */
468 GstDPPayloadType
gst_dp_header_payload_type(const guint8 * header)469 gst_dp_header_payload_type (const guint8 * header)
470 {
471   g_return_val_if_fail (header != NULL, GST_DP_PAYLOAD_NONE);
472 
473   return GST_DP_HEADER_PAYLOAD_TYPE (header);
474 }
475 
476 /*** DEPACKETIZING FUNCTIONS ***/
477 
478 /**
479  * gst_dp_buffer_from_header:
480  * @header_length: the length of the packet header
481  * @header: the byte array of the packet header
482  * @allocator: the allocator used to allocate the new #GstBuffer
483  * @allocation_params: the allocations parameters used to allocate the new #GstBuffer
484  *
485  * Creates a newly allocated #GstBuffer from the given header.
486  * The buffer data needs to be copied into it before validating.
487  *
488  * Use this function if you want to pre-allocate a buffer based on the
489  * packet header to read the packet payload in to.
490  *
491  * This function does not check the header passed to it, use
492  * gst_dp_validate_header() first if the header data is unchecked.
493  *
494  * Returns: A #GstBuffer if the buffer was successfully created, or NULL.
495  */
496 GstBuffer *
gst_dp_buffer_from_header(guint header_length,const guint8 * header,GstAllocator * allocator,GstAllocationParams * allocation_params)497 gst_dp_buffer_from_header (guint header_length, const guint8 * header,
498     GstAllocator * allocator, GstAllocationParams * allocation_params)
499 {
500   GstBuffer *buffer;
501 
502   g_return_val_if_fail (header != NULL, NULL);
503   g_return_val_if_fail (header_length >= GST_DP_HEADER_LENGTH, NULL);
504   g_return_val_if_fail (GST_DP_HEADER_PAYLOAD_TYPE (header) ==
505       GST_DP_PAYLOAD_BUFFER, NULL);
506 
507   buffer =
508       gst_buffer_new_allocate (allocator,
509       (guint) GST_DP_HEADER_PAYLOAD_LENGTH (header), allocation_params);
510 
511   GST_BUFFER_TIMESTAMP (buffer) = GST_DP_HEADER_TIMESTAMP (header);
512   GST_BUFFER_DTS (buffer) = GST_DP_HEADER_DTS (header);
513   GST_BUFFER_DURATION (buffer) = GST_DP_HEADER_DURATION (header);
514   GST_BUFFER_OFFSET (buffer) = GST_DP_HEADER_OFFSET (header);
515   GST_BUFFER_OFFSET_END (buffer) = GST_DP_HEADER_OFFSET_END (header);
516   GST_BUFFER_FLAGS (buffer) = GST_DP_HEADER_BUFFER_FLAGS (header);
517 
518   return buffer;
519 }
520 
521 /**
522  * gst_dp_caps_from_packet:
523  * @header_length: the length of the packet header
524  * @header: the byte array of the packet header
525  * @payload: the byte array of the packet payload
526  *
527  * Creates a newly allocated #GstCaps from the given packet.
528  *
529  * This function does not check the arguments passed to it, use
530  * gst_dp_validate_packet() first if the header and payload data are
531  * unchecked.
532  *
533  * Returns: A #GstCaps containing the caps represented in the packet,
534  *          or NULL if the packet could not be converted.
535  */
536 GstCaps *
gst_dp_caps_from_packet(guint header_length,const guint8 * header,const guint8 * payload)537 gst_dp_caps_from_packet (guint header_length, const guint8 * header,
538     const guint8 * payload)
539 {
540   GstCaps *caps;
541   gchar *string;
542 
543   g_return_val_if_fail (header, NULL);
544   g_return_val_if_fail (header_length >= GST_DP_HEADER_LENGTH, NULL);
545   g_return_val_if_fail (GST_DP_HEADER_PAYLOAD_TYPE (header) ==
546       GST_DP_PAYLOAD_CAPS, NULL);
547   g_return_val_if_fail (payload, NULL);
548 
549   /* 0 sized payload length will work create NULL string */
550   string = g_strndup ((gchar *) payload, GST_DP_HEADER_PAYLOAD_LENGTH (header));
551   caps = gst_caps_from_string (string);
552   g_free (string);
553 
554   return caps;
555 }
556 
557 static GstEvent *
gst_dp_event_from_packet_0_2(guint header_length,const guint8 * header,const guint8 * payload)558 gst_dp_event_from_packet_0_2 (guint header_length, const guint8 * header,
559     const guint8 * payload)
560 {
561   GstEvent *event = NULL;
562   GstEventType type;
563 
564   type = GST_DP_HEADER_PAYLOAD_TYPE (header) - GST_DP_PAYLOAD_EVENT_NONE;
565   switch (type) {
566     case GST_EVENT_UNKNOWN:
567       GST_WARNING ("Unknown event, ignoring");
568       return NULL;
569     case GST_EVENT_EOS:
570     case GST_EVENT_FLUSH_START:
571     case GST_EVENT_FLUSH_STOP:
572     case GST_EVENT_SEGMENT:
573       event = gst_event_new_custom (type, NULL);
574       break;
575     case GST_EVENT_SEEK:
576     {
577       gdouble rate;
578       GstFormat format;
579       GstSeekFlags flags;
580       GstSeekType start_type, stop_type;
581       gint64 start, stop;
582 
583       g_return_val_if_fail (payload != NULL, NULL);
584 
585       /* FIXME, read rate */
586       rate = 1.0;
587       format = (GstFormat) GST_READ_UINT32_BE (payload);
588       flags = (GstSeekFlags) GST_READ_UINT32_BE (payload + 4);
589       start_type = (GstSeekType) GST_READ_UINT32_BE (payload + 8);
590       start = (gint64) GST_READ_UINT64_BE (payload + 12);
591       stop_type = (GstSeekType) GST_READ_UINT32_BE (payload + 20);
592       stop = (gint64) GST_READ_UINT64_BE (payload + 24);
593 
594       event = gst_event_new_seek (rate, format, flags, start_type, start,
595           stop_type, stop);
596       GST_EVENT_TIMESTAMP (event) = GST_DP_HEADER_TIMESTAMP (header);
597       break;
598     }
599     case GST_EVENT_QOS:
600     case GST_EVENT_NAVIGATION:
601     case GST_EVENT_TAG:
602       GST_WARNING ("Unhandled event type %d, ignoring", type);
603       return NULL;
604     default:
605       GST_WARNING ("Unknown event type %d, ignoring", type);
606       return NULL;
607   }
608 
609   return event;
610 }
611 
612 static GstEvent *
gst_dp_event_from_packet_1_0(guint header_length,const guint8 * header,const guint8 * payload)613 gst_dp_event_from_packet_1_0 (guint header_length, const guint8 * header,
614     const guint8 * payload)
615 {
616   GstEvent *event = NULL;
617   GstEventType type;
618   gchar *string = NULL;
619   GstStructure *s = NULL;
620 
621   type = GST_DP_HEADER_PAYLOAD_TYPE (header) - GST_DP_PAYLOAD_EVENT_NONE;
622   if (payload) {
623     string =
624         g_strndup ((gchar *) payload, GST_DP_HEADER_PAYLOAD_LENGTH (header));
625     s = gst_structure_from_string (string, NULL);
626     if (s == NULL) {
627       GST_WARNING ("Could not parse payload string: %s", string);
628       g_free (string);
629       return NULL;
630     }
631 
632     g_free (string);
633   }
634   GST_LOG ("Creating event of type 0x%x with structure '%" GST_PTR_FORMAT "'",
635       type, s);
636   event = gst_event_new_custom (type, s);
637   return event;
638 }
639 
640 
641 /**
642  * gst_dp_event_from_packet:
643  * @header_length: the length of the packet header
644  * @header: the byte array of the packet header
645  * @payload: the byte array of the packet payload
646  *
647  * Creates a newly allocated #GstEvent from the given packet.
648  *
649  * This function does not check the arguments passed to it, use
650  * gst_dp_validate_packet() first if the header and payload data are
651  * unchecked.
652  *
653  * Returns: A #GstEvent if the event was successfully created,
654  *          or NULL if an event could not be read from the payload.
655  */
656 GstEvent *
gst_dp_event_from_packet(guint header_length,const guint8 * header,const guint8 * payload)657 gst_dp_event_from_packet (guint header_length, const guint8 * header,
658     const guint8 * payload)
659 {
660   guint8 major, minor;
661 
662   g_return_val_if_fail (header, NULL);
663   g_return_val_if_fail (header_length >= GST_DP_HEADER_LENGTH, NULL);
664 
665   major = GST_DP_HEADER_MAJOR_VERSION (header);
666   minor = GST_DP_HEADER_MINOR_VERSION (header);
667 
668   if (major == 0 && minor == 2)
669     return gst_dp_event_from_packet_0_2 (header_length, header, payload);
670   else if (major == 1 && minor == 0)
671     return gst_dp_event_from_packet_1_0 (header_length, header, payload);
672   else {
673     GST_ERROR ("Unknown GDP version %d.%d", major, minor);
674     return NULL;
675   }
676 }
677 
678 /**
679  * gst_dp_validate_header:
680  * @header_length: the length of the packet header
681  * @header: the byte array of the packet header
682  *
683  * Validates the given packet header by checking the CRC checksum.
684  *
685  * Returns: %TRUE if the CRC matches, or no CRC checksum is present.
686  */
687 gboolean
gst_dp_validate_header(guint header_length,const guint8 * header)688 gst_dp_validate_header (guint header_length, const guint8 * header)
689 {
690   guint16 crc_read, crc_calculated;
691 
692   g_return_val_if_fail (header != NULL, FALSE);
693   g_return_val_if_fail (header_length >= GST_DP_HEADER_LENGTH, FALSE);
694 
695   if (!(GST_DP_HEADER_FLAGS (header) & GST_DP_HEADER_FLAG_CRC_HEADER))
696     return TRUE;
697 
698   crc_read = GST_DP_HEADER_CRC_HEADER (header);
699 
700   /* don't include the last two crc fields for the crc check */
701   crc_calculated = gst_dp_crc (header, header_length - 4);
702   if (crc_read != crc_calculated)
703     goto crc_error;
704 
705   GST_LOG ("header crc validation: %02x", crc_read);
706   return TRUE;
707 
708   /* ERRORS */
709 crc_error:
710   {
711     GST_WARNING ("header crc mismatch: read %02x, calculated %02x", crc_read,
712         crc_calculated);
713     return FALSE;
714   }
715 }
716 
717 /**
718  * gst_dp_validate_payload:
719  * @header_length: the length of the packet header
720  * @header: the byte array of the packet header
721  * @payload: the byte array of the packet payload
722  *
723  * Validates the given packet payload using the given packet header
724  * by checking the CRC checksum.
725  *
726  * Returns: %TRUE if the CRC matches, or no CRC checksum is present.
727  */
728 gboolean
gst_dp_validate_payload(guint header_length,const guint8 * header,const guint8 * payload)729 gst_dp_validate_payload (guint header_length, const guint8 * header,
730     const guint8 * payload)
731 {
732   guint16 crc_read, crc_calculated;
733 
734   g_return_val_if_fail (header != NULL, FALSE);
735   g_return_val_if_fail (header_length >= GST_DP_HEADER_LENGTH, FALSE);
736 
737   if (!(GST_DP_HEADER_FLAGS (header) & GST_DP_HEADER_FLAG_CRC_PAYLOAD))
738     return TRUE;
739 
740   crc_read = GST_DP_HEADER_CRC_PAYLOAD (header);
741   crc_calculated = gst_dp_crc (payload, GST_DP_HEADER_PAYLOAD_LENGTH (header));
742   if (crc_read != crc_calculated)
743     goto crc_error;
744 
745   GST_LOG ("payload crc validation: %02x", crc_read);
746   return TRUE;
747 
748   /* ERRORS */
749 crc_error:
750   {
751     GST_WARNING ("payload crc mismatch: read %02x, calculated %02x", crc_read,
752         crc_calculated);
753     return FALSE;
754   }
755 }
756 
757 /**
758  * gst_dp_validate_packet:
759  * @header_length: the length of the packet header
760  * @header: the byte array of the packet header
761  * @payload: the byte array of the packet payload
762  *
763  * Validates the given packet by checking version information and checksums.
764  *
765  * Returns: %TRUE if the packet validates.
766  */
767 gboolean
gst_dp_validate_packet(guint header_length,const guint8 * header,const guint8 * payload)768 gst_dp_validate_packet (guint header_length, const guint8 * header,
769     const guint8 * payload)
770 {
771   if (!gst_dp_validate_header (header_length, header))
772     return FALSE;
773   if (!gst_dp_validate_payload (header_length, header, payload))
774     return FALSE;
775 
776   return TRUE;
777 }
778