Port gtk-doc comments to their equivalent markdown syntax
[platform/upstream/gstreamer.git] / libs / gst / net / gstnettimepacket.c
1 /* GStreamer
2  * Copyright (C) 2005 Andy Wingo <wingo@pobox.com>
3  * Copyright (C) 2010 Tim-Philipp Müller <tim centricular net>
4  * Copyright (C) 2012 Collabora Ltd. <tim.muller@collabora.co.uk>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 /**
22  * SECTION:gstnettimepacket
23  * @title: GstNetTimePacket
24  * @short_description: Helper structure to construct clock packets used
25  *                     by network clocks.
26  * @see_also: #GstClock, #GstNetClientClock, #GstNetTimeProvider
27  *
28  * Various functions for receiving, sending an serializing #GstNetTimePacket
29  * structures.
30  */
31
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35
36 #include <glib.h>
37
38 #ifdef __CYGWIN__
39 # include <unistd.h>
40 # include <fcntl.h>
41 #endif
42
43 #include "gstnettimepacket.h"
44
45 G_DEFINE_BOXED_TYPE (GstNetTimePacket, gst_net_time_packet,
46     gst_net_time_packet_copy, gst_net_time_packet_free);
47
48 /**
49  * gst_net_time_packet_new:
50  * @buffer: (array): a buffer from which to construct the packet, or NULL
51  *
52  * Creates a new #GstNetTimePacket from a buffer received over the network. The
53  * caller is responsible for ensuring that @buffer is at least
54  * #GST_NET_TIME_PACKET_SIZE bytes long.
55  *
56  * If @buffer is #NULL, the local and remote times will be set to
57  * #GST_CLOCK_TIME_NONE.
58  *
59  * MT safe. Caller owns return value (gst_net_time_packet_free to free).
60  *
61  * Returns: The new #GstNetTimePacket.
62  */
63 GstNetTimePacket *
64 gst_net_time_packet_new (const guint8 * buffer)
65 {
66   GstNetTimePacket *ret;
67
68   g_assert (sizeof (GstClockTime) == 8);
69
70   ret = g_new0 (GstNetTimePacket, 1);
71
72   if (buffer) {
73     ret->local_time = GST_READ_UINT64_BE (buffer);
74     ret->remote_time = GST_READ_UINT64_BE (buffer + sizeof (GstClockTime));
75   } else {
76     ret->local_time = GST_CLOCK_TIME_NONE;
77     ret->remote_time = GST_CLOCK_TIME_NONE;
78   }
79
80   return ret;
81 }
82
83 /**
84  * gst_net_time_packet_free:
85  * @packet: the #GstNetTimePacket
86  *
87  * Free @packet.
88  */
89 void
90 gst_net_time_packet_free (GstNetTimePacket * packet)
91 {
92   g_free (packet);
93 }
94
95 /**
96  * gst_net_time_packet_copy:
97  * @packet: the #GstNetTimePacket
98  *
99  * Make a copy of @packet.
100  *
101  * Returns: a copy of @packet, free with gst_net_time_packet_free().
102  */
103 GstNetTimePacket *
104 gst_net_time_packet_copy (const GstNetTimePacket * packet)
105 {
106   GstNetTimePacket *ret;
107
108   ret = g_new0 (GstNetTimePacket, 1);
109   ret->local_time = packet->local_time;
110   ret->remote_time = packet->remote_time;
111
112   return ret;
113 }
114
115 /**
116  * gst_net_time_packet_serialize:
117  * @packet: the #GstNetTimePacket
118  *
119  * Serialized a #GstNetTimePacket into a newly-allocated sequence of
120  * #GST_NET_TIME_PACKET_SIZE bytes, in network byte order. The value returned is
121  * suitable for passing to write(2) or sendto(2) for communication over the
122  * network.
123  *
124  * MT safe. Caller owns return value (g_free to free).
125  *
126  * Returns: A newly allocated sequence of #GST_NET_TIME_PACKET_SIZE bytes.
127  */
128 guint8 *
129 gst_net_time_packet_serialize (const GstNetTimePacket * packet)
130 {
131   guint8 *ret;
132
133   g_assert (sizeof (GstClockTime) == 8);
134
135   ret = g_new0 (guint8, GST_NET_TIME_PACKET_SIZE);
136
137   GST_WRITE_UINT64_BE (ret, packet->local_time);
138   GST_WRITE_UINT64_BE (ret + sizeof (GstClockTime), packet->remote_time);
139
140   return ret;
141 }
142
143 /**
144  * gst_net_time_packet_receive:
145  * @socket: socket to receive the time packet on
146  * @src_address: (out): address of variable to return sender address
147  * @error: return address for a #GError, or NULL
148  *
149  * Receives a #GstNetTimePacket over a socket. Handles interrupted system
150  * calls, but otherwise returns NULL on error.
151  *
152  * Returns: (transfer full): a new #GstNetTimePacket, or NULL on error. Free
153  *    with gst_net_time_packet_free() when done.
154  */
155 GstNetTimePacket *
156 gst_net_time_packet_receive (GSocket * socket,
157     GSocketAddress ** src_address, GError ** error)
158 {
159   gchar buffer[GST_NET_TIME_PACKET_SIZE];
160   GError *err = NULL;
161   gssize ret;
162
163   g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
164   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
165
166   while (TRUE) {
167     ret = g_socket_receive_from (socket, src_address, buffer,
168         GST_NET_TIME_PACKET_SIZE, NULL, &err);
169
170     if (ret < 0) {
171       if (err->code == G_IO_ERROR_WOULD_BLOCK) {
172         g_error_free (err);
173         err = NULL;
174         continue;
175       } else {
176         goto receive_error;
177       }
178     } else if (ret < GST_NET_TIME_PACKET_SIZE) {
179       goto short_packet;
180     } else {
181       return gst_net_time_packet_new ((const guint8 *) buffer);
182     }
183   }
184
185 receive_error:
186   {
187     GST_DEBUG ("receive error: %s", err->message);
188     g_propagate_error (error, err);
189     return NULL;
190   }
191 short_packet:
192   {
193     GST_DEBUG ("someone sent us a short packet (%" G_GSSIZE_FORMAT " < %d)",
194         ret, GST_NET_TIME_PACKET_SIZE);
195     g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
196         "short time packet (%d < %d)", (int) ret, GST_NET_TIME_PACKET_SIZE);
197     return NULL;
198   }
199 }
200
201 /**
202  * gst_net_time_packet_send:
203  * @packet: the #GstNetTimePacket to send
204  * @socket: socket to send the time packet on
205  * @dest_address: address to send the time packet to
206  * @error: return address for a #GError, or NULL
207  *
208  * Sends a #GstNetTimePacket over a socket.
209  *
210  * MT safe.
211  *
212  * Returns: TRUE if successful, FALSE in case an error occurred.
213  */
214 gboolean
215 gst_net_time_packet_send (const GstNetTimePacket * packet,
216     GSocket * socket, GSocketAddress * dest_address, GError ** error)
217 {
218   gboolean was_blocking;
219   guint8 *buffer;
220   gssize res;
221
222   g_return_val_if_fail (packet != NULL, FALSE);
223   g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
224   g_return_val_if_fail (G_IS_SOCKET_ADDRESS (dest_address), FALSE);
225   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
226
227   was_blocking = g_socket_get_blocking (socket);
228
229   if (was_blocking)
230     g_socket_set_blocking (socket, FALSE);
231
232   /* FIXME: avoid pointless alloc/free, serialise into stack-allocated buffer */
233   buffer = gst_net_time_packet_serialize (packet);
234
235   res = g_socket_send_to (socket, dest_address, (const gchar *) buffer,
236       GST_NET_TIME_PACKET_SIZE, NULL, error);
237
238   /* datagram packets should be sent as a whole or not at all */
239   g_assert (res < 0 || res == GST_NET_TIME_PACKET_SIZE);
240
241   g_free (buffer);
242
243   if (was_blocking)
244     g_socket_set_blocking (socket, TRUE);
245
246   return (res == GST_NET_TIME_PACKET_SIZE);
247 }