1 /********************************************************************
3 * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
4 * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
5 * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE. *
6 * PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
8 * THE Ogg123 SOURCE CODE IS (C) COPYRIGHT 2000-2001 *
9 * by Stan Seibert <volsung@xiph.org> AND OTHER CONTRIBUTORS *
10 * http://www.xiph.org/ *
12 ********************************************************************
14 last mod: $Id: http_transport.c,v 1.12 2003/08/06 23:14:12 volsung Exp $
16 ********************************************************************/
28 #include <curl/curl.h>
29 #include <curl/easy.h>
33 #include "transport.h"
36 #include "callbacks.h"
39 #define INPUT_BUFFER_SIZE 32768
41 extern stat_format_t
*stat_format
; /* FIXME Bad hack! Will fix after RC3! */
42 extern signal_request_t sig_request
; /* Need access to global cancel flag */
44 typedef struct http_private_t
{
49 pthread_t curl_thread
;
52 struct curl_slist
*header_list
;
53 char error
[CURL_ERROR_SIZE
];
55 data_source_t
*data_source
;
56 data_source_stats_t stats
;
60 transport_t http_transport
; /* Forward declaration */
62 /* -------------------------- curl callbacks ----------------------- */
64 size_t write_callback (void *ptr
, size_t size
, size_t nmemb
, void *arg
)
66 http_private_t
*myarg
= arg
;
68 if (myarg
->cancel_flag
|| sig_request
.cancel
)
71 buffer_submit_data(myarg
->buf
, ptr
, size
*nmemb
);
73 if (myarg
->cancel_flag
|| sig_request
.cancel
)
79 int progress_callback (void *arg
, size_t dltotal
, size_t dlnow
,
80 size_t ultotal
, size_t ulnow
)
82 http_private_t
*myarg
= arg
;
83 print_statistics_arg_t
*pstats_arg
;
84 data_source_t
*source
= myarg
->data_source
;
86 if (myarg
->cancel_flag
|| sig_request
.cancel
)
89 pstats_arg
= new_print_statistics_arg(stat_format
,
90 source
->transport
->statistics(source
),
93 print_statistics_action(NULL
, pstats_arg
);
95 if (myarg
->cancel_flag
|| sig_request
.cancel
)
102 /* -------------------------- Private functions --------------------- */
104 void set_curl_opts (http_private_t
*private)
106 CURL
*handle
= private->curl_handle
;
108 curl_easy_setopt(handle
, CURLOPT_FILE
, private);
109 curl_easy_setopt(handle
, CURLOPT_WRITEFUNCTION
, write_callback
);
110 curl_easy_setopt(handle
, CURLOPT_URL
, private->data_source
->source_string
);
112 if (inputOpts.ProxyPort)
113 curl_easy_setopt(handle, CURLOPT_PROXYPORT, inputOpts.ProxyPort);
114 if (inputOpts.ProxyHost)
115 curl_easy_setopt(handle, CURLOPT_PROXY, inputOpts.ProxyHost);
116 if (inputOpts.ProxyTunnel)
117 curl_easy_setopt (handle, CURLOPT_HTTPPROXYTUNNEL, inputOpts.ProxyTunnel);
120 curl_easy_setopt(handle
, CURLOPT_MUTE
, 1);
122 curl_easy_setopt(handle
, CURLOPT_ERRORBUFFER
, private->error
);
123 curl_easy_setopt(handle
, CURLOPT_PROGRESSFUNCTION
, progress_callback
);
124 curl_easy_setopt(handle
, CURLOPT_PROGRESSDATA
, private);
125 curl_easy_setopt(handle
, CURLOPT_NOPROGRESS
, 0);
126 curl_easy_setopt(handle
, CURLOPT_USERAGENT
, "ogg123/"VERSION
);
127 curl_easy_setopt(handle
, CURLOPT_HTTPHEADER
, private->header_list
);
128 curl_easy_setopt(handle
, CURLOPT_FOLLOWLOCATION
, 1);
132 void *curl_thread_func (void *arg
)
134 http_private_t
*myarg
= (http_private_t
*) arg
;
138 /* Block signals to this thread */
140 sigaddset (&set
, SIGINT
);
141 sigaddset (&set
, SIGTSTP
);
142 sigaddset (&set
, SIGCONT
);
143 if (pthread_sigmask (SIG_BLOCK
, &set
, NULL
) != 0)
144 status_error(_("Error: Could not set signal mask."));
146 ret
= curl_easy_perform((CURL
*) myarg
->curl_handle
);
148 if (myarg
->cancel_flag
|| sig_request
.cancel
) {
149 buffer_abort_write(myarg
->buf
);
150 ret
= 0; // "error" was on purpose
152 buffer_mark_eos(myarg
->buf
);
155 status_error(myarg
->error
);
157 curl_easy_cleanup(myarg
->curl_handle
);
158 myarg
->curl_handle
= 0;
160 curl_slist_free_all(myarg
->header_list
);
161 myarg
->header_list
= NULL
;
167 /* -------------------------- Public interface -------------------------- */
169 int http_can_transport (char *source_string
)
173 tmp
= strchr(source_string
, ':') - source_string
;
175 tmp
+ 2 < strlen(source_string
) &&
176 !strncmp(source_string
+ tmp
, "://", 3);
180 data_source_t
* http_open (char *source_string
, ogg123_options_t
*ogg123_opts
)
182 data_source_t
*source
;
183 http_private_t
*private;
185 /* Allocate data source structures */
186 source
= malloc(sizeof(data_source_t
));
187 private = malloc(sizeof(http_private_t
));
189 if (source
!= NULL
&& private != NULL
) {
190 source
->source_string
= strdup(source_string
);
191 source
->transport
= &http_transport
;
192 source
->private = private;
194 private->buf
= buffer_create (ogg123_opts
->input_buffer_size
,
195 ogg123_opts
->input_buffer_size
*
196 ogg123_opts
->input_prebuffer
/ 100.0,
197 NULL
, NULL
, /* No write callback, using
198 buffer in pull mode. */
200 if (private->buf
== NULL
) {
201 status_error(_("Error: Unable to create input buffer.\n"));
205 private->curl_handle
= NULL
;
206 private->header_list
= NULL
;
207 private->data_source
= source
;
208 private->stats
.transfer_rate
= 0;
209 private->stats
.bytes_read
= 0;
210 private->stats
.input_buffer_used
= 0;
211 private->cancel_flag
= 0;
214 fprintf(stderr
, _("Error: Out of memory.\n"));
218 /* ogg123 only accepts Ogg files, and optionally FLAC as well */
220 private->header_list
= curl_slist_append(NULL
, "Accept: application/ogg, audio/ogg, video/ogg, audio/x-flac;q=0.9");
222 private->header_list
= curl_slist_append(NULL
, "Accept: application/ogg, audio/ogg, video/ogg;q=0.9");
224 if (private->header_list
== NULL
)
228 private->curl_handle
= curl_easy_init();
229 if (private->curl_handle
== NULL
)
232 set_curl_opts(private);
235 if (pthread_create(&private->curl_thread
, NULL
, curl_thread_func
,
240 stat_format
[2].enabled
= 0; /* remaining playback time */
241 stat_format
[3].enabled
= 0; /* total playback time */
242 stat_format
[6].enabled
= 1; /* Input buffer fill % */
243 stat_format
[7].enabled
= 1; /* Input buffer state */
249 if (private->curl_handle
!= NULL
)
250 curl_easy_cleanup(private->curl_handle
);
251 if (private->header_list
!= NULL
)
252 curl_slist_free_all(private->header_list
);
253 free(source
->source_string
);
261 int http_peek (data_source_t
*source
, void *ptr
, size_t size
, size_t nmemb
)
264 http_private_t *private = source->private;
268 bytes_read = buffer_get_data(data->buf, ptr, size, nmemb);
270 private->stats.bytes_read += bytes_read;
277 int http_read (data_source_t
*source
, void *ptr
, size_t size
, size_t nmemb
)
279 http_private_t
*private = source
->private;
282 if (private->cancel_flag
|| sig_request
.cancel
)
285 bytes_read
= buffer_get_data(private->buf
, ptr
, size
* nmemb
);
287 private->stats
.bytes_read
+= bytes_read
;
293 int http_seek (data_source_t
*source
, long offset
, int whence
)
299 data_source_stats_t
*http_statistics (data_source_t
*source
)
301 http_private_t
*private = source
->private;
302 data_source_stats_t
*data_source_stats
;
303 buffer_stats_t
*buffer_stats
;
305 data_source_stats
= malloc_data_source_stats(&private->stats
);
306 data_source_stats
->input_buffer_used
= 1;
307 data_source_stats
->transfer_rate
= 0;
309 buffer_stats
= buffer_statistics(private->buf
);
310 data_source_stats
->input_buffer
= *buffer_stats
;
313 return data_source_stats
;
317 long http_tell (data_source_t
*source
)
323 void http_close (data_source_t
*source
)
325 http_private_t
*private = source
->private;
327 private->cancel_flag
= 1;
328 buffer_abort_write(private->buf
);
329 pthread_join(private->curl_thread
, NULL
);
331 buffer_destroy(private->buf
);
334 free(source
->source_string
);
335 free(source
->private);
340 transport_t http_transport
= {
352 #endif /* HAVE_CURL */