blob: f914988d31d5c02e7967240bec807ad5653eb061 [file] [log] [blame]
/* GStreamer
* Copyright (C) 2005 Andy Wingo <wingo@pobox.com>
* Copyright (C) 2010 Tim-Philipp Müller <tim centricular net>
* Copyright (C) 2012 Collabora Ltd. <tim.muller@collabora.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:gstnettimepacket
* @title: GstNetTimePacket
* @short_description: Helper structure to construct clock packets used
* by network clocks.
* @see_also: #GstClock, #GstNetClientClock, #GstNetTimeProvider
*
* Various functions for receiving, sending an serializing #GstNetTimePacket
* structures.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <glib.h>
#ifdef __CYGWIN__
# include <unistd.h>
# include <fcntl.h>
#endif
#include "gstnettimepacket.h"
G_DEFINE_BOXED_TYPE (GstNetTimePacket, gst_net_time_packet,
gst_net_time_packet_copy, gst_net_time_packet_free);
/**
* gst_net_time_packet_new:
* @buffer: (array): a buffer from which to construct the packet, or NULL
*
* Creates a new #GstNetTimePacket from a buffer received over the network. The
* caller is responsible for ensuring that @buffer is at least
* #GST_NET_TIME_PACKET_SIZE bytes long.
*
* If @buffer is %NULL, the local and remote times will be set to
* #GST_CLOCK_TIME_NONE.
*
* MT safe. Caller owns return value (gst_net_time_packet_free to free).
*
* Returns: The new #GstNetTimePacket.
*/
GstNetTimePacket *
gst_net_time_packet_new (const guint8 * buffer)
{
GstNetTimePacket *ret;
g_assert (sizeof (GstClockTime) == 8);
ret = g_new0 (GstNetTimePacket, 1);
if (buffer) {
ret->local_time = GST_READ_UINT64_BE (buffer);
ret->remote_time = GST_READ_UINT64_BE (buffer + sizeof (GstClockTime));
} else {
ret->local_time = GST_CLOCK_TIME_NONE;
ret->remote_time = GST_CLOCK_TIME_NONE;
}
return ret;
}
/**
* gst_net_time_packet_free:
* @packet: the #GstNetTimePacket
*
* Free @packet.
*/
void
gst_net_time_packet_free (GstNetTimePacket * packet)
{
g_free (packet);
}
/**
* gst_net_time_packet_copy:
* @packet: the #GstNetTimePacket
*
* Make a copy of @packet.
*
* Returns: a copy of @packet, free with gst_net_time_packet_free().
*/
GstNetTimePacket *
gst_net_time_packet_copy (const GstNetTimePacket * packet)
{
GstNetTimePacket *ret;
ret = g_new0 (GstNetTimePacket, 1);
ret->local_time = packet->local_time;
ret->remote_time = packet->remote_time;
return ret;
}
/**
* gst_net_time_packet_serialize:
* @packet: the #GstNetTimePacket
*
* Serialized a #GstNetTimePacket into a newly-allocated sequence of
* #GST_NET_TIME_PACKET_SIZE bytes, in network byte order. The value returned is
* suitable for passing to write(2) or sendto(2) for communication over the
* network.
*
* MT safe. Caller owns return value (g_free to free).
*
* Returns: A newly allocated sequence of #GST_NET_TIME_PACKET_SIZE bytes.
*/
guint8 *
gst_net_time_packet_serialize (const GstNetTimePacket * packet)
{
guint8 *ret;
g_assert (sizeof (GstClockTime) == 8);
ret = g_new0 (guint8, GST_NET_TIME_PACKET_SIZE);
GST_WRITE_UINT64_BE (ret, packet->local_time);
GST_WRITE_UINT64_BE (ret + sizeof (GstClockTime), packet->remote_time);
return ret;
}
/**
* gst_net_time_packet_receive:
* @socket: socket to receive the time packet on
* @src_address: (out): address of variable to return sender address
* @error: return address for a #GError, or NULL
*
* Receives a #GstNetTimePacket over a socket. Handles interrupted system
* calls, but otherwise returns NULL on error.
*
* Returns: (transfer full): a new #GstNetTimePacket, or NULL on error. Free
* with gst_net_time_packet_free() when done.
*/
GstNetTimePacket *
gst_net_time_packet_receive (GSocket * socket,
GSocketAddress ** src_address, GError ** error)
{
gchar buffer[GST_NET_TIME_PACKET_SIZE];
GError *err = NULL;
gssize ret;
g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
while (TRUE) {
ret = g_socket_receive_from (socket, src_address, buffer,
GST_NET_TIME_PACKET_SIZE, NULL, &err);
if (ret < 0) {
if (err->code == G_IO_ERROR_WOULD_BLOCK) {
g_error_free (err);
err = NULL;
continue;
} else {
goto receive_error;
}
} else if (ret < GST_NET_TIME_PACKET_SIZE) {
goto short_packet;
} else {
return gst_net_time_packet_new ((const guint8 *) buffer);
}
}
receive_error:
{
GST_DEBUG ("receive error: %s", err->message);
g_propagate_error (error, err);
return NULL;
}
short_packet:
{
GST_DEBUG ("someone sent us a short packet (%" G_GSSIZE_FORMAT " < %d)",
ret, GST_NET_TIME_PACKET_SIZE);
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
"short time packet (%d < %d)", (int) ret, GST_NET_TIME_PACKET_SIZE);
return NULL;
}
}
/**
* gst_net_time_packet_send:
* @packet: the #GstNetTimePacket to send
* @socket: socket to send the time packet on
* @dest_address: address to send the time packet to
* @error: return address for a #GError, or NULL
*
* Sends a #GstNetTimePacket over a socket.
*
* MT safe.
*
* Returns: TRUE if successful, FALSE in case an error occurred.
*/
gboolean
gst_net_time_packet_send (const GstNetTimePacket * packet,
GSocket * socket, GSocketAddress * dest_address, GError ** error)
{
gboolean was_blocking;
guint8 *buffer;
gssize res;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
g_return_val_if_fail (G_IS_SOCKET_ADDRESS (dest_address), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
was_blocking = g_socket_get_blocking (socket);
if (was_blocking)
g_socket_set_blocking (socket, FALSE);
/* FIXME: avoid pointless alloc/free, serialise into stack-allocated buffer */
buffer = gst_net_time_packet_serialize (packet);
res = g_socket_send_to (socket, dest_address, (const gchar *) buffer,
GST_NET_TIME_PACKET_SIZE, NULL, error);
/* datagram packets should be sent as a whole or not at all */
g_assert (res < 0 || res == GST_NET_TIME_PACKET_SIZE);
g_free (buffer);
if (was_blocking)
g_socket_set_blocking (socket, TRUE);
return (res == GST_NET_TIME_PACKET_SIZE);
}