Merge branch 'master' into 0.11
[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., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 /**
22  * SECTION:gstnettimepacket
23  * @short_description: Helper structure to construct clock packets used
24  *                     by network clocks.
25  * @see_also: #GstClock, #GstNetClientClock, #GstNetTimeProvider
26  *
27  * Various functions for receiving, sending an serializing #GstNetTimePacket
28  * structures.
29  *
30  * Last reviewed on 2005-11-23 (0.9.5)
31  */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include <glib.h>
38
39 #ifdef __CYGWIN__
40 # include <unistd.h>
41 # include <fcntl.h>
42 #endif
43
44 #include "gstnettimepacket.h"
45
46
47 /**
48  * gst_net_time_packet_new:
49  * @buffer: a buffer from which to construct the packet, or NULL
50  *
51  * Creates a new #GstNetTimePacket from a buffer received over the network. The
52  * caller is responsible for ensuring that @buffer is at least
53  * #GST_NET_TIME_PACKET_SIZE bytes long.
54  *
55  * If @buffer is #NULL, the local and remote times will be set to
56  * #GST_CLOCK_TIME_NONE.
57  *
58  * MT safe. Caller owns return value (g_free to free).
59  *
60  * Returns: The new #GstNetTimePacket.
61  */
62 GstNetTimePacket *
63 gst_net_time_packet_new (const guint8 * buffer)
64 {
65   GstNetTimePacket *ret;
66
67   g_assert (sizeof (GstClockTime) == 8);
68
69   ret = g_new0 (GstNetTimePacket, 1);
70
71   if (buffer) {
72     ret->local_time = GST_READ_UINT64_BE (buffer);
73     ret->remote_time = GST_READ_UINT64_BE (buffer + sizeof (GstClockTime));
74   } else {
75     ret->local_time = GST_CLOCK_TIME_NONE;
76     ret->remote_time = GST_CLOCK_TIME_NONE;
77   }
78
79   return ret;
80 }
81
82 /**
83  * gst_net_time_packet_serialize:
84  * @packet: the #GstNetTimePacket
85  *
86  * Serialized a #GstNetTimePacket into a newly-allocated sequence of
87  * #GST_NET_TIME_PACKET_SIZE bytes, in network byte order. The value returned is
88  * suitable for passing to write(2) or sendto(2) for communication over the
89  * network.
90  *
91  * MT safe. Caller owns return value (g_free to free).
92  *
93  * Returns: A newly allocated sequence of #GST_NET_TIME_PACKET_SIZE bytes.
94  */
95 guint8 *
96 gst_net_time_packet_serialize (const GstNetTimePacket * packet)
97 {
98   guint8 *ret;
99
100   g_assert (sizeof (GstClockTime) == 8);
101
102   ret = g_new0 (guint8, GST_NET_TIME_PACKET_SIZE);
103
104   GST_WRITE_UINT64_BE (ret, packet->local_time);
105   GST_WRITE_UINT64_BE (ret + sizeof (GstClockTime), packet->remote_time);
106
107   return ret;
108 }
109
110 /**
111  * gst_net_time_packet_receive:
112  * @socket: socket to receive the time packet on
113  * @src_addr: (out): address of variable to return sender address
114  * @err: return address for a #GError, or NULL
115  *
116  * Receives a #GstNetTimePacket over a socket. Handles interrupted system
117  * calls, but otherwise returns NULL on error.
118  *
119  * Returns: (transfer full): a new #GstNetTimePacket, or NULL on error. Free
120  *    with g_free() when done.
121  */
122 GstNetTimePacket *
123 gst_net_time_packet_receive (GSocket * socket,
124     GSocketAddress ** src_address, GError ** error)
125 {
126   gchar buffer[GST_NET_TIME_PACKET_SIZE];
127   GError *err = NULL;
128   gssize ret;
129
130   g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
131   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
132
133   while (TRUE) {
134     ret = g_socket_receive_from (socket, src_address, buffer,
135         GST_NET_TIME_PACKET_SIZE, NULL, &err);
136
137     if (ret < 0) {
138       if (err->code == G_IO_ERROR_WOULD_BLOCK) {
139         g_error_free (err);
140         err = NULL;
141         continue;
142       } else {
143         goto receive_error;
144       }
145     } else if (ret < GST_NET_TIME_PACKET_SIZE) {
146       goto short_packet;
147     } else {
148       return gst_net_time_packet_new ((const guint8 *) buffer);
149     }
150   }
151
152 receive_error:
153   {
154     GST_DEBUG ("receive error: %s", err->message);
155     g_propagate_error (error, err);
156     return NULL;
157   }
158 short_packet:
159   {
160     GST_DEBUG ("someone sent us a short packet (%d < %d)",
161         ret, GST_NET_TIME_PACKET_SIZE);
162     g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
163         "short time packet (%d < %d)", (int) ret, GST_NET_TIME_PACKET_SIZE);
164     return NULL;
165   }
166 }
167
168 /**
169  * gst_net_time_packet_send:
170  * @packet: the #GstNetTimePacket to send
171  * @socket: socket to send the time packet on
172  * @dest_addr: address to send the time packet to
173  * @err: return address for a #GError, or NULL
174  *
175  * Sends a #GstNetTimePacket over a socket.
176  *
177  * MT safe.
178  *
179  * Returns: TRUE if successful, FALSE in case an error occured.
180  */
181 gboolean
182 gst_net_time_packet_send (const GstNetTimePacket * packet,
183     GSocket * socket, GSocketAddress * dest_address, GError ** error)
184 {
185   gboolean was_blocking;
186   guint8 *buffer;
187   gssize res;
188
189   g_return_val_if_fail (packet != NULL, FALSE);
190   g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
191   g_return_val_if_fail (G_IS_SOCKET_ADDRESS (dest_address), FALSE);
192   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
193
194   was_blocking = g_socket_get_blocking (socket);
195
196   if (was_blocking)
197     g_socket_set_blocking (socket, FALSE);
198
199   /* FIXME: avoid pointless alloc/free, serialise into stack-allocated buffer */
200   buffer = gst_net_time_packet_serialize (packet);
201
202   res = g_socket_send_to (socket, dest_address, (const gchar *) buffer,
203       GST_NET_TIME_PACKET_SIZE, NULL, error);
204
205   /* datagram packets should be sent as a whole or not at all */
206   g_assert (res < 0 || res == GST_NET_TIME_PACKET_SIZE);
207
208   g_free (buffer);
209
210   if (was_blocking)
211     g_socket_set_blocking (socket, TRUE);
212
213   return (res == GST_NET_TIME_PACKET_SIZE);
214 }