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 ********************************************************************
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 if (!buffer_submit_data(myarg
->buf
, ptr
, size
*nmemb
))
74 if (myarg
->cancel_flag
|| sig_request
.cancel
)
80 int progress_callback (void *arg
, size_t dltotal
, size_t dlnow
,
81 size_t ultotal
, size_t ulnow
)
83 http_private_t
*myarg
= arg
;
84 print_statistics_arg_t
*pstats_arg
;
85 data_source_t
*source
= myarg
->data_source
;
87 if (myarg
->cancel_flag
|| sig_request
.cancel
)
90 pstats_arg
= new_print_statistics_arg(stat_format
,
91 source
->transport
->statistics(source
),
94 print_statistics_action(NULL
, pstats_arg
);
96 if (myarg
->cancel_flag
|| sig_request
.cancel
)
103 /* -------------------------- Private functions --------------------- */
105 void set_curl_opts (http_private_t
*private)
107 CURL
*handle
= private->curl_handle
;
109 curl_easy_setopt(handle
, CURLOPT_FILE
, private);
110 curl_easy_setopt(handle
, CURLOPT_WRITEFUNCTION
, write_callback
);
111 curl_easy_setopt(handle
, CURLOPT_URL
, private->data_source
->source_string
);
113 if (inputOpts.ProxyPort)
114 curl_easy_setopt(handle, CURLOPT_PROXYPORT, inputOpts.ProxyPort);
115 if (inputOpts.ProxyHost)
116 curl_easy_setopt(handle, CURLOPT_PROXY, inputOpts.ProxyHost);
117 if (inputOpts.ProxyTunnel)
118 curl_easy_setopt (handle, CURLOPT_HTTPPROXYTUNNEL, inputOpts.ProxyTunnel);
121 curl_easy_setopt(handle
, CURLOPT_MUTE
, 1);
123 curl_easy_setopt(handle
, CURLOPT_ERRORBUFFER
, private->error
);
124 curl_easy_setopt(handle
, CURLOPT_PROGRESSFUNCTION
, progress_callback
);
125 curl_easy_setopt(handle
, CURLOPT_PROGRESSDATA
, private);
126 curl_easy_setopt(handle
, CURLOPT_NOPROGRESS
, 0);
127 curl_easy_setopt(handle
, CURLOPT_USERAGENT
, "ogg123/"VERSION
);
128 curl_easy_setopt(handle
, CURLOPT_HTTPHEADER
, private->header_list
);
129 curl_easy_setopt(handle
, CURLOPT_FOLLOWLOCATION
, 1);
133 void *curl_thread_func (void *arg
)
135 http_private_t
*myarg
= (http_private_t
*) arg
;
139 /* Block signals to this thread */
141 sigaddset (&set
, SIGINT
);
142 sigaddset (&set
, SIGTSTP
);
143 sigaddset (&set
, SIGCONT
);
144 if (pthread_sigmask (SIG_BLOCK
, &set
, NULL
) != 0)
145 status_error(_("ERROR: Could not set signal mask."));
147 ret
= curl_easy_perform((CURL
*) myarg
->curl_handle
);
149 if (myarg
->cancel_flag
|| sig_request
.cancel
) {
150 buffer_abort_write(myarg
->buf
);
151 ret
= 0; // "error" was on purpose
153 buffer_mark_eos(myarg
->buf
);
156 status_error(myarg
->error
);
158 curl_easy_cleanup(myarg
->curl_handle
);
159 myarg
->curl_handle
= 0;
161 curl_slist_free_all(myarg
->header_list
);
162 myarg
->header_list
= NULL
;
168 /* -------------------------- Public interface -------------------------- */
170 int http_can_transport (char *source_string
)
174 tmp
= strchr(source_string
, ':') - source_string
;
176 tmp
+ 2 < strlen(source_string
) &&
177 !strncmp(source_string
+ tmp
, "://", 3);
181 data_source_t
* http_open (char *source_string
, ogg123_options_t
*ogg123_opts
)
183 data_source_t
*source
;
184 http_private_t
*private;
186 /* Allocate data source structures */
187 source
= malloc(sizeof(data_source_t
));
188 private = malloc(sizeof(http_private_t
));
190 if (source
!= NULL
&& private != NULL
) {
191 source
->source_string
= strdup(source_string
);
192 source
->transport
= &http_transport
;
193 source
->private = private;
195 private->buf
= buffer_create (ogg123_opts
->input_buffer_size
,
196 ogg123_opts
->input_buffer_size
*
197 ogg123_opts
->input_prebuffer
/ 100.0,
198 NULL
, NULL
, /* No write callback, using
199 buffer in pull mode. */
201 if (private->buf
== NULL
) {
202 status_error(_("ERROR: Unable to create input buffer.\n"));
206 private->curl_handle
= NULL
;
207 private->header_list
= NULL
;
208 private->data_source
= source
;
209 private->stats
.transfer_rate
= 0;
210 private->stats
.bytes_read
= 0;
211 private->stats
.input_buffer_used
= 0;
212 private->cancel_flag
= 0;
215 fprintf(stderr
, _("ERROR: Out of memory.\n"));
219 /* ogg123 only accepts Ogg files, and optionally FLAC as well */
221 private->header_list
= curl_slist_append(NULL
, "Accept: application/ogg, audio/ogg, video/ogg, audio/x-flac;q=0.9");
223 private->header_list
= curl_slist_append(NULL
, "Accept: application/ogg, audio/ogg, video/ogg;q=0.9");
225 if (private->header_list
== NULL
)
229 private->curl_handle
= curl_easy_init();
230 if (private->curl_handle
== NULL
)
233 set_curl_opts(private);
236 if (pthread_create(&private->curl_thread
, NULL
, curl_thread_func
,
241 stat_format
[2].enabled
= 0; /* remaining playback time */
242 stat_format
[3].enabled
= 0; /* total playback time */
243 stat_format
[6].enabled
= 1; /* Input buffer fill % */
244 stat_format
[7].enabled
= 1; /* Input buffer state */
250 if (private->curl_handle
!= NULL
)
251 curl_easy_cleanup(private->curl_handle
);
252 if (private->header_list
!= NULL
)
253 curl_slist_free_all(private->header_list
);
254 free(source
->source_string
);
262 int http_peek (data_source_t
*source
, void *ptr
, size_t size
, size_t nmemb
)
265 http_private_t *private = source->private;
269 bytes_read = buffer_get_data(data->buf, ptr, size, nmemb);
271 private->stats.bytes_read += bytes_read;
278 int http_read (data_source_t
*source
, void *ptr
, size_t size
, size_t nmemb
)
280 http_private_t
*private = source
->private;
283 if (private->cancel_flag
|| sig_request
.cancel
)
286 bytes_read
= buffer_get_data(private->buf
, ptr
, size
* nmemb
);
288 private->stats
.bytes_read
+= bytes_read
;
294 int http_seek (data_source_t
*source
, long offset
, int whence
)
300 data_source_stats_t
*http_statistics (data_source_t
*source
)
302 http_private_t
*private = source
->private;
303 data_source_stats_t
*data_source_stats
;
304 buffer_stats_t
*buffer_stats
;
306 data_source_stats
= malloc_data_source_stats(&private->stats
);
307 data_source_stats
->input_buffer_used
= 1;
308 data_source_stats
->transfer_rate
= 0;
310 buffer_stats
= buffer_statistics(private->buf
);
311 data_source_stats
->input_buffer
= *buffer_stats
;
314 return data_source_stats
;
318 long http_tell (data_source_t
*source
)
324 void http_close (data_source_t
*source
)
326 http_private_t
*private = source
->private;
328 private->cancel_flag
= 1;
329 buffer_abort_write(private->buf
);
330 pthread_join(private->curl_thread
, NULL
);
332 buffer_destroy(private->buf
);
335 free(source
->source_string
);
336 free(source
->private);
341 transport_t http_transport
= {
353 #endif /* HAVE_CURL */