icecast_client.c
Go to the documentation of this file.
1 /**
2  * @file icecast_client.c
3  * @brief Icecast client
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2019 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 1.9.6
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL ICECAST_TRACE_LEVEL
33 
34 //Dependencies
35 #include <stdlib.h>
36 #include "icecast/icecast_client.h"
37 #include "str.h"
38 #include "debug.h"
39 
40 //Check TCP/IP stack configuration
41 #if (ICECAST_CLIENT_SUPPORT == ENABLED)
42 
43 
44 /**
45  * @brief Initialize settings with default values
46  * @param[out] settings Structure that contains Icecast client settings
47  **/
48 
50 {
51  //Use default interface
52  settings->interface = netGetDefaultInterface();
53 
54  //Icecast server name
55  strcpy(settings->serverName, "");
56  //Icecast server port
57  settings->serverPort = 8000;
58  //Requested resource
59  strcpy(settings->resource, "/stream");
60 
61  //Streaming buffer size
62  settings->bufferSize = 80000;
63 }
64 
65 
66 /**
67  * @brief Icecast client initialization
68  * @param[in] context Pointer to the Icecast client context
69  * @param[in] settings Icecast client specific settings
70  * @return Error code
71  **/
72 
74  const IcecastClientSettings *settings)
75 {
76  error_t error;
77 
78  //Debug message
79  TRACE_INFO("Initializing Icecast client...\r\n");
80 
81  //Ensure the parameters are valid
82  if(!context || !settings)
84 
85  //Clear the Icecast client context
86  memset(context, 0, sizeof(IcecastClientContext));
87 
88  //Save user settings
89  context->settings = *settings;
90  //Get the size of the circular buffer
91  context->bufferSize = settings->bufferSize;
92 
93  //Start of exception handling block
94  do
95  {
96  //Allocate a memory block to hold the circular buffer
97  context->streamBuffer = osAllocMem(context->bufferSize);
98  //Failed to allocate memory?
99  if(!context->streamBuffer)
100  {
101  //Report an error to the calling function
102  error = ERROR_OUT_OF_MEMORY;
103  break;
104  }
105 
106  //Create mutex object to protect critical sections
107  if(!osCreateMutex(&context->mutex))
108  {
109  //Failed to create mutex
110  error = ERROR_OUT_OF_RESOURCES;
111  break;
112  }
113 
114  //Create event to get notified when the buffer is writable
115  if(!osCreateEvent(&context->writeEvent))
116  {
117  //Failed to create event object
118  error = ERROR_OUT_OF_RESOURCES;
119  break;
120  }
121 
122  //The buffer is available for writing
123  osSetEvent(&context->writeEvent);
124 
125  //Create event to get notified when the buffer is readable
126  if(!osCreateEvent(&context->readEvent))
127  {
128  //Failed to create event object
129  error = ERROR_OUT_OF_RESOURCES;
130  break;
131  }
132 
133  //Successful initialization
134  error = NO_ERROR;
135 
136  //End of exception handling block
137  } while(0);
138 
139  //Check whether an error occurred
140  if(error)
141  {
142  //Clean up side effects...
143  osFreeMem(context->streamBuffer);
144  osDeleteMutex(&context->mutex);
145  osDeleteEvent(&context->writeEvent);
146  osDeleteEvent(&context->readEvent);
147  }
148 
149  //Return status code
150  return error;
151 }
152 
153 
154 /**
155  * @brief Start Icecast client
156  * @param[in] context Pointer to the Icecast client context
157  * @return Error code
158  **/
159 
161 {
162  OsTask *task;
163 
164  //Debug message
165  TRACE_INFO("Starting Icecast client...\r\n");
166 
167  //Create the Icecast client task
168  task = osCreateTask("Icecast client", icecastClientTask,
170 
171  //Unable to create the task?
172  if(task == OS_INVALID_HANDLE)
173  return ERROR_OUT_OF_RESOURCES;
174 
175  //Successful processing
176  return NO_ERROR;
177 }
178 
179 
180 /**
181  * @brief Copy data from input stream
182  * @param[in] context Pointer to the Icecast client context
183  * @param[out] data Pointer to the user buffer
184  * @param[in] size Maximum number of bytes that can be read
185  * @param[out] length Number of bytes that have been read
186  * @param[in] timeout Maximum time to wait before returning
187  * @return Error code
188  **/
189 
191  uint8_t *data, size_t size, size_t *length, systime_t timeout)
192 {
193  bool_t status;
194 
195  //Ensure the parameters are valid
196  if(!context || !data)
198 
199  //Wait for the buffer to be available for reading
200  status = osWaitForEvent(&context->readEvent, timeout);
201  //Timeout error?
202  if(!status)
203  return ERROR_TIMEOUT;
204 
205  //Enter critical section
206  osAcquireMutex(&context->mutex);
207  //Compute the number of bytes to read at a time
208  *length = MIN(size, context->bufferLength);
209  //Leave critical section
210  osReleaseMutex(&context->mutex);
211 
212  //Check whether the specified data crosses buffer boundaries
213  if((context->readIndex + *length) <= context->bufferSize)
214  {
215  //Copy the data
216  memcpy(data, context->streamBuffer + context->readIndex, *length);
217  }
218  else
219  {
220  //Copy the first part of the data
221  memcpy(data, context->streamBuffer + context->readIndex,
222  context->bufferSize - context->readIndex);
223  //Wrap around to the beginning of the circular buffer
224  memcpy(data + context->bufferSize - context->readIndex, context->streamBuffer,
225  *length - context->bufferSize + context->readIndex);
226  }
227 
228  //Enter critical section
229  osAcquireMutex(&context->mutex);
230 
231  //Increment read index
232  context->readIndex += *length;
233  //Wrap around if necessary
234  if(context->readIndex >= context->bufferSize)
235  context->readIndex -= context->bufferSize;
236 
237  //Update buffer length
238  context->bufferLength -= *length;
239  //Check whether the buffer is available for writing
240  if(context->bufferLength < context->bufferSize)
241  osSetEvent(&context->writeEvent);
242  //Check whether the buffer is available for reading
243  if(context->bufferLength > 0)
244  osSetEvent(&context->readEvent);
245 
246  //Leave critical section
247  osReleaseMutex(&context->mutex);
248 
249  //Successful read operation
250  return NO_ERROR;
251 }
252 
253 
254 /**
255  * @brief Copy metadata from input stream
256  * @param[in] context Pointer to the Icecast client context
257  * @param[out] metadata Pointer to the user buffer
258  * @param[in] size Maximum number of bytes that can be read
259  * @param[out] length Number of bytes that have been read
260  * @return Error code
261  **/
262 
264  char_t *metadata, size_t size, size_t *length)
265 {
266  //Ensure the parameters are valid
267  if(!context || !metadata)
269 
270  //Enter critical section
271  osAcquireMutex(&context->mutex);
272 
273  //Limit the number of data to read
274  *length = MIN(size, context->metadataLength);
275  //Save metadata information
276  memcpy(metadata, context->metadata, *length);
277 
278  //Leave critical section
279  osReleaseMutex(&context->mutex);
280 
281  //Successful read operation
282  return NO_ERROR;
283 }
284 
285 
286 /**
287  * @brief Icecast client task
288  * @param[in] param Pointer to the Icecast client context
289  **/
290 
291 void icecastClientTask(void *param)
292 {
293  error_t error;
294  bool_t end;
295  size_t n;
296  size_t length;
297  size_t received;
298  IcecastClientContext *context;
299 
300  //Task prologue
301  osEnterTask();
302 
303  //Retrieve the Icecast client context
304  context = (IcecastClientContext *) param;
305 
306  //Main loop
307  while(1)
308  {
309  //Debug message
310  TRACE_INFO("Icecast client: Connecting to server %s port %" PRIu16 "\r\n",
311  context->settings.serverName, context->settings.serverPort);
312 
313  //Initiate a connection to the Icecast server
314  error = icecastClientConnect(context);
315 
316  //Connection to server failed?
317  if(error)
318  {
319  //Debug message
320  TRACE_ERROR("Icecast client: Connection to server failed!\r\n");
321  //Recovery delay
323  //Try to reconnect...
324  continue;
325  }
326 
327  //Debug message
328  TRACE_INFO("Block size = %" PRIuSIZE "\r\n", context->blockSize);
329 
330  //Check block size
331  if(!context->blockSize)
332  {
333  //Close socket
334  socketClose(context->socket);
335  //Recovery delay
337  //Try to reconnect...
338  continue;
339  }
340 
341  //Initialize loop condition variable
342  end = FALSE;
343 
344  //Read as much data as possible...
345  while(!end)
346  {
347  //Process the stream block by block
348  length = context->blockSize;
349 
350  //Read current block
351  while(!end && length > 0)
352  {
353  //Wait for the buffer to be available for writing
355 
356  //Enter critical section
357  osAcquireMutex(&context->mutex);
358  //Compute the number of bytes to read at a time
359  n = MIN(length, context->bufferSize - context->bufferLength);
360  //Leave critical section
361  osReleaseMutex(&context->mutex);
362 
363  //Check whether the specified data crosses buffer boundaries
364  if((context->writeIndex + n) > context->bufferSize)
365  n = context->bufferSize - context->writeIndex;
366 
367  //Receive data
368  error = socketReceive(context->socket, context->streamBuffer +
369  context->writeIndex, n, &received, SOCKET_FLAG_WAIT_ALL);
370 
371  //Any error to report?
372  if(error)
373  {
374  //Stop streaming data
375  end = TRUE;
376  }
377  else
378  {
379  //Enter critical section
380  osAcquireMutex(&context->mutex);
381 
382  //Increment write index
383  context->writeIndex += n;
384  //Wrap around if necessary
385  if(context->writeIndex >= context->bufferSize)
386  context->writeIndex -= context->bufferSize;
387 
388  //Update buffer length
389  context->bufferLength += n;
390  //Check whether the buffer is available for writing
391  if(context->bufferLength < context->bufferSize)
392  osSetEvent(&context->writeEvent);
393  //Check whether the buffer is available for reading
394  if(context->bufferLength > 0)
395  osSetEvent(&context->readEvent);
396 
397  //Leave critical section
398  osReleaseMutex(&context->mutex);
399 
400  //Update the total number of bytes that have been received
401  context->totalLength += n;
402  //Number of remaining data to read
403  length -= n;
404  }
405  }
406 
407  //Debug message
408  TRACE_DEBUG("Icecast client: Total bytes received = %" PRIuSIZE "\r\n", context->totalLength);
409 
410  //Check whether the metadata block should be read
411  if(!end)
412  {
413  //Process the metadata block
414  error = icecastClientProcessMetadata(context);
415  //Any error to report?
416  if(error)
417  end = TRUE;
418  }
419  }
420 
421  //Close connection
422  socketClose(context->socket);
423  }
424 }
425 
426 
427 /**
428  * @brief Connect to the specified Icecast server
429  * @param[in] context Pointer to the Icecast client context
430  **/
431 
433 {
434  error_t error;
435  size_t length;
436  uint16_t serverPort;
437  IpAddr serverIpAddr;
438  NetInterface *interface;
439 
440  //Underlying network interface
441  interface = context->settings.interface;
442 
443  //Force traffic to go through a proxy server?
444  if(strcmp(interface->proxyName, ""))
445  {
446  //Icecast request template
447  const char_t requestTemplate[] =
448  "GET http://%s:%" PRIu16 "%s HTTP/1.1\r\n"
449  "Host: %s:%" PRIu16 "\r\n"
450  "User-agent: UserAgent\r\n"
451  "Icy-MetaData: 1\r\n"
452  "Connection: close\r\n"
453  "\r\n";
454 
455  //Format Icecast request
456  length = sprintf(context->buffer, requestTemplate,
457  context->settings.serverName, context->settings.serverPort,
458  context->settings.resource, context->settings.serverName,
459  context->settings.serverPort);
460 
461  //The specified proxy server can be either an IP or a host name
462  error = getHostByName(interface, interface->proxyName, &serverIpAddr, 0);
463  //Unable to resolve server name?
464  if(error)
465  return error;
466 
467  //Proxy server port
468  serverPort = interface->proxyPort;
469  }
470  else
471  {
472  //Icecast request template
473  const char_t requestTemplate[] =
474  "GET %s HTTP/1.1\r\n"
475  "Host: %s\r\n"
476  "User-agent: UserAgent\r\n"
477  "Icy-MetaData: 1\r\n"
478  "Connection: close\r\n"
479  "\r\n";
480 
481  //Format Icecast request
482  length = sprintf(context->buffer, requestTemplate,
483  context->settings.resource, context->settings.serverName);
484 
485  //The specified Icecast server can be either an IP or a host name
486  error = getHostByName(interface, context->settings.serverName, &serverIpAddr, 0);
487  //Unable to resolve server name?
488  if(error)
489  return error;
490 
491  //Icecast server port
492  serverPort = context->settings.serverPort;
493  }
494 
495  //Open a TCP socket
497  //Failed to open socket?
498  if(context->socket == NULL)
499  return ERROR_OUT_OF_RESOURCES;
500 
501  //Start of exception handling block
502  do
503  {
504  //Associate the socket with the relevant interface
505  error = socketBindToInterface(context->socket, interface);
506  //Unable to bind the socket to the desired interface?
507  if(error)
508  break;
509 
510  //Adjust receive timeout
512  //Any error to report?
513  if(error)
514  break;
515 
516  //Connect to the server
517  error = socketConnect(context->socket, &serverIpAddr, serverPort);
518  //Connection with server failed?
519  if(error)
520  break;
521 
522  //Display Icecast request for debugging purpose
523  TRACE_DEBUG(context->buffer);
524 
525  //Send request to the server
526  error = socketSend(context->socket, context->buffer,
528  //Failed to send the request?
529  if(error)
530  break;
531 
532  //Parse response header
533  while(1)
534  {
535  char_t *separator;
536  char_t *property;
537  char_t *value;
538 
539  //Read a line from the response header
540  error = socketReceive(context->socket, context->buffer,
542  //Failed to read data?
543  if(error)
544  break;
545 
546  //Properly terminate the string with a NULL character
547  context->buffer[length] = '\0';
548 
549  //The end of the header has been reached?
550  if(!strcmp(context->buffer, "\r\n"))
551  break;
552 
553  //Check whether a separator is present
554  separator = strchr(context->buffer, ':');
555 
556  //Separator found?
557  if(separator)
558  {
559  //Split the line
560  *separator = '\0';
561 
562  //Get property name and value
563  property = strTrimWhitespace(context->buffer);
564  value = strTrimWhitespace(separator + 1);
565 
566  //Debug message
567  TRACE_INFO("<%s>=<%s>\r\n", property, value);
568 
569  //Icy-Metaint property found?
570  if(!strcasecmp(property, "Icy-Metaint"))
571  {
572  //Retrieve the block size used by the Icecast server
573  context->blockSize = atoi(value);
574  }
575  }
576  }
577 
578  //End of exception handling block
579  } while(0);
580 
581  //Check whether an error occurred
582  if(error)
583  {
584  //Clean up side effects
585  socketClose(context->socket);
586  }
587 
588  //Return status code
589  return error;
590 }
591 
592 
593 /**
594  * @brief Decode metadata block
595  * @param[in] context Pointer to the Icecast client context
596  **/
597 
599 {
600  error_t error;
601  size_t n;
602  size_t length;
603  size_t metadataLength;
604 
605  //The metadata block begins with a single byte which indicates
606  //how many 16-byte segments need to be read
607  error = socketReceive(context->socket, context->buffer,
608  sizeof(uint8_t), &length, SOCKET_FLAG_WAIT_ALL);
609 
610  //Any error to report?
611  if(error)
612  return error;
613  //Make sure the expected number of bytes have been received
614  if(length != sizeof(uint8_t))
615  return ERROR_INVALID_METADATA;
616 
617  //Compute the length of the following metadata block
618  metadataLength = context->buffer[0] * 16;
619  //Limit the number of bytes to read
620  n = MIN(metadataLength, ICECAST_CLIENT_METADATA_MAX_SIZE - 1);
621 
622  //Read the metadata information
623  error = socketReceive(context->socket, context->buffer,
625 
626  //Any error to report?
627  if(error)
628  return error;
629  //Make sure the expected number of bytes have been received
630  if(length != n)
631  return ERROR_INVALID_METADATA;
632 
633  //Enter critical section
634  osAcquireMutex(&context->mutex);
635 
636  //Save metadata information
637  memcpy(context->metadata, context->buffer, n);
638  //Terminate the string properly
639  context->metadata[n] = '\0';
640  //Record the length of the metadata
641  context->metadataLength = n;
642 
643  //Leave critical section
644  osReleaseMutex(&context->mutex);
645 
646  //Any metadata information received?
647  if(n > 0)
648  {
649  //Debug message
650  TRACE_DEBUG("Icecast client: Metadata = <%s>\r\n", context->metadata);
651  }
652 
653  //Compute the number of bytes that have not been processed
654  metadataLength -= n;
655 
656  //Read the complete metadata
657  while(metadataLength > 0)
658  {
659  //Compute the number of data to read at a time
660  n = MIN(metadataLength, ICECAST_CLIENT_METADATA_MAX_SIZE);
661 
662  //Drop incoming data...
663  error = socketReceive(context->socket, context->buffer,
665 
666  //Any error to report?
667  if(error)
668  return error;
669  //Make sure the expected number of bytes have been received
670  if(length != n)
671  return ERROR_INVALID_METADATA;
672 
673  //Update byte counter
674  metadataLength -= n;
675  }
676 
677  //Successful processing
678  return NO_ERROR;
679 }
680 
681 #endif
682 
error_t socketSend(Socket *socket, const void *data, size_t length, size_t *written, uint_t flags)
Send data to a connected socket.
Definition: socket.c:514
size_t metadataLength
Length of the metadata.
size_t bufferLength
Streaming buffer length.
uint8_t length
Definition: dtls_misc.h:149
String manipulation helper functions.
Icecast client context.
error_t icecastClientStart(IcecastClientContext *context)
Start Icecast client.
int bool_t
Definition: compiler_port.h:49
@ SOCKET_FLAG_WAIT_ALL
Definition: socket.h:120
bool_t osCreateMutex(OsMutex *mutex)
Create a mutex object.
size_t writeIndex
Current write index within the buffer.
IP network address.
Definition: ip.h:71
Icecast client settings.
char_t * strTrimWhitespace(char_t *s)
Removes all leading and trailing whitespace from a string.
Definition: str.c:78
@ SOCKET_FLAG_WAIT_ACK
Definition: socket.h:124
#define TRUE
Definition: os_port.h:50
void socketClose(Socket *socket)
Close an existing socket.
Definition: socket.c:822
error_t icecastClientProcessMetadata(IcecastClientContext *context)
Decode metadata block.
@ ERROR_OUT_OF_RESOURCES
Definition: error.h:64
@ ERROR_OUT_OF_MEMORY
Definition: error.h:63
size_t totalLength
Total number of bytes that have been received.
char_t serverName[ICECAST_SERVER_NAME_MAX_LEN]
Icecast server name.
@ SOCKET_TYPE_STREAM
Definition: socket.h:77
IcecastClientSettings settings
User settings.
#define TRACE_ERROR(...)
Definition: debug.h:74
error_t icecastClientInit(IcecastClientContext *context, const IcecastClientSettings *settings)
Icecast client initialization.
error_t icecastClientReadMetadata(IcecastClientContext *context, char_t *metadata, size_t size, size_t *length)
Copy metadata from input stream.
#define FALSE
Definition: os_port.h:46
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
OsTask * osCreateTask(const char_t *name, OsTaskCode taskCode, void *param, size_t stackSize, int_t priority)
Create a new task.
#define ICECAST_CLIENT_STACK_SIZE
error_t
Error codes.
Definition: error.h:42
OsMutex mutex
Mutex protecting critical sections.
error_t socketReceive(Socket *socket, void *data, size_t size, size_t *received, uint_t flags)
Receive data from a connected socket.
Definition: socket.c:609
size_t bufferSize
Streaming buffer size.
char_t buffer[ICECAST_CLIENT_METADATA_MAX_SIZE]
Memory buffer for input/output operations.
void icecastClientTask(void *param)
Icecast client task.
char_t resource[ICECAST_RESOURCE_MAX_LEN]
Requested resource.
void osDeleteEvent(OsEvent *event)
Delete an event object.
#define NetInterface
Definition: net.h:36
Task object.
#define ICECAST_CLIENT_TIMEOUT
NetInterface * netGetDefaultInterface(void)
Get default network interface.
Definition: net.c:1782
NetInterface * interface
Underlying network interface.
error_t getHostByName(NetInterface *interface, const char_t *name, IpAddr *ipAddr, uint_t flags)
Resolve a host name into an IP address.
Definition: socket.c:1078
error_t socketConnect(Socket *socket, const IpAddr *remoteIpAddr, uint16_t remotePort)
Establish a connection to a specified socket.
Definition: socket.c:381
void * osAllocMem(size_t size)
Allocate a memory block.
#define TRACE_INFO(...)
Definition: debug.h:94
Socket * socketOpen(uint_t type, uint_t protocol)
Create a socket (UDP or TCP)
Definition: socket.c:95
char_t metadata[ICECAST_CLIENT_METADATA_MAX_SIZE]
Metadata information.
#define MIN(a, b)
Definition: os_port.h:62
#define osEnterTask()
OsEvent readEvent
This event tells whether the buffer is readable.
#define socketBindToInterface
Definition: net_legacy.h:270
@ ERROR_INVALID_METADATA
Definition: error.h:214
#define TRACE_DEBUG(...)
Definition: debug.h:106
@ ERROR_TIMEOUT
Definition: error.h:94
char char_t
Definition: compiler_port.h:43
#define OS_INVALID_HANDLE
Definition: os_port.h:79
void osDeleteMutex(OsMutex *mutex)
Delete a mutex object.
void icecastClientGetDefaultSettings(IcecastClientSettings *settings)
Initialize settings with default values.
uint8_t n
bool_t osWaitForEvent(OsEvent *event, systime_t timeout)
Wait until the specified event is in the signaled state.
void osAcquireMutex(OsMutex *mutex)
Acquire ownership of the specified mutex object.
void osReleaseMutex(OsMutex *mutex)
Release ownership of the specified mutex object.
error_t icecastClientReadStream(IcecastClientContext *context, uint8_t *data, size_t size, size_t *length, systime_t timeout)
Copy data from input stream.
#define ICECAST_RECOVERY_DELAY
Icecast client.
bool_t osCreateEvent(OsEvent *event)
Create an event object.
OsEvent writeEvent
This event tells whether the buffer is writable.
Socket * socket
Underlying socket.
#define strcasecmp
#define ICECAST_CLIENT_PRIORITY
void osDelayTask(systime_t delay)
Delay routine.
void osSetEvent(OsEvent *event)
Set the specified event object to the signaled state.
size_t readIndex
Current read index within the buffer.
size_t blockSize
Number of data bytes between subsequent metadata blocks.
uint8_t value[]
Definition: dtls_misc.h:150
#define PRIuSIZE
Definition: compiler_port.h:78
size_t bufferSize
Streaming buffer size.
uint8_t data[]
Definition: dtls_misc.h:176
@ SOCKET_IP_PROTO_TCP
Definition: socket.h:92
error_t socketSetTimeout(Socket *socket, systime_t timeout)
Set timeout value for blocking operations.
Definition: socket.c:219
#define ICECAST_CLIENT_METADATA_MAX_SIZE
@ SOCKET_FLAG_BREAK_CRLF
Definition: socket.h:123
uint32_t systime_t
Definition: compiler_port.h:46
uint16_t serverPort
Icecast server port.
void osFreeMem(void *p)
Release a previously allocated memory block.
uint8_t * streamBuffer
Streaming buffer.
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
#define INFINITE_DELAY
Definition: os_port.h:74
error_t icecastClientConnect(IcecastClientContext *context)
Connect to the specified Icecast server.