summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorGravatar Alexey Yakovenko <wakeroid@gmail.com>2010-01-31 20:26:17 +0100
committerGravatar Alexey Yakovenko <wakeroid@gmail.com>2010-01-31 20:26:17 +0100
commit6e6dc88fc61f4a84269a592c76c2b6bd0417bf4a (patch)
tree8b5b1fe103a9a4e93a680493d9f1dd2fcb71ded2 /plugins
parent9460844e2a1598dfe24a72dd2a283d02b57caddc (diff)
parent893655b27084db6399b82069a5dcb56d6e98fbfa (diff)
Merge branch 'master' into devel
Conflicts: main.c plugins/flac/flac.c plugins/mpgmad/mpgmad.c streamer.c
Diffstat (limited to 'plugins')
-rw-r--r--plugins/alsa/alsa.c10
-rw-r--r--plugins/flac/flac.c77
-rw-r--r--plugins/mpgmad/mpgmad.c133
-rw-r--r--plugins/vfs_curl/vfs_curl.c308
4 files changed, 374 insertions, 154 deletions
diff --git a/plugins/alsa/alsa.c b/plugins/alsa/alsa.c
index 2017907c..ef1a4782 100644
--- a/plugins/alsa/alsa.c
+++ b/plugins/alsa/alsa.c
@@ -182,10 +182,12 @@ palsa_set_hw_params (int samplerate) {
req_period_size = deadbeef->conf_get_int ("alsa.period", 512);
trace ("trying buffer size: %d frames\n", req_buffer_size);
trace ("trying period size: %d frames\n", req_period_size);
- snd_pcm_hw_params_set_buffer_size_near (audio, hw_params, &buffer_size);
- snd_pcm_hw_params_set_period_size_near (audio, hw_params, &period_size, NULL);
- trace ("alsa buffer size: %d frames\n", buffer_size);
- trace ("alsa period size: %d frames\n", period_size);
+ snd_pcm_hw_params_set_buffer_size_near (audio, hw_params, &req_buffer_size);
+ snd_pcm_hw_params_set_period_size_near (audio, hw_params, &req_period_size, NULL);
+ trace ("alsa buffer size: %d frames\n", req_buffer_size);
+ trace ("alsa period size: %d frames\n", req_period_size);
+ buffer_size = req_buffer_size;
+ period_size = req_period_size;
if ((err = snd_pcm_hw_params (audio, hw_params)) < 0) {
trace ("cannot set parameters (%s)\n",
diff --git a/plugins/flac/flac.c b/plugins/flac/flac.c
index dbb355d3..173df64c 100644
--- a/plugins/flac/flac.c
+++ b/plugins/flac/flac.c
@@ -112,20 +112,21 @@ cflac_write_callback (const FLAC__StreamDecoder *decoder, const FLAC__Frame *fra
if (bitrate > 0) {
deadbeef->streamer_set_bitrate (bitrate/1000);
}
- int readbytes = frame->header.blocksize * _info->channels * _info->bps / 8;
int bufsize = BUFFERSIZE - info->remaining;
int bufsamples = bufsize / (_info->channels * _info->bps / 8);
int nsamples = min (bufsamples, frame->header.blocksize);
char *bufptr = &info->buffer[info->remaining];
float mul = 1.f/ ((1 << (_info->bps-1))-1);
+ int channels = _info->channels;
+ if (channels > 2) {
+ channels = 2;
+ }
+ int readbytes = frame->header.blocksize * channels * _info->bps / 8;
+
for (int i = 0; i < nsamples; i++) {
- int32_t sample = inputbuffer[0][i];
- *((float*)bufptr) = sample * mul;
- bufptr += sizeof (float);
- info->remaining += sizeof (float);
- if (_info->channels > 1) {
- int32_t sample = inputbuffer[1][i];
+ for (int c = 0; c < channels; c++) {
+ int32_t sample = inputbuffer[c][i];
*((float*)bufptr) = sample * mul;
bufptr += sizeof (float);
info->remaining += sizeof (float);
@@ -311,25 +312,39 @@ cflac_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) {
return 0;
}
}
+ int n_output_channels = _info->channels;
+ if (n_output_channels > 2) {
+ n_output_channels = 2;
+ }
int initsize = size;
do {
if (info->remaining) {
- int s = size * 2;
- int sz = min (info->remaining, s);
+ int n_input_frames = info->remaining / sizeof (float) / n_output_channels;
+ int n_output_frames = size / n_output_channels / sizeof (int16_t);
+ int n = min (n_input_frames, n_output_frames);
+
+ trace ("flac: [1] if=%d, of=%d, n=%d, rem=%d, size=%d\n", n_input_frames, n_output_frames, n, remaining, size);
// convert from float to int16
float *in = (float *)info->buffer;
- for (int i = 0; i < sz/4; i++) {
+ for (int i = 0; i < n; i++) {
*((int16_t *)bytes) = (int16_t)((*in) * 0x7fff);
- size -= 2;
- bytes += 2;
- in++;
+ size -= sizeof (int16_t);
+ bytes += sizeof (int16_t);
+ if (n_output_channels == 2) {
+ *((int16_t *)bytes) = (int16_t)((*(in+1)) * 0x7fff);
+ size -= sizeof (int16_t);
+ bytes += sizeof (int16_t);
+ }
+ in += n_output_channels;
}
+ int sz = n * sizeof (float) * n_output_channels;
if (sz < info->remaining) {
- memmove (info->buffer, &info->buffer[sz], info->remaining-sz);
+ memmove (info->buffer, &info->buffer[sz], info->remaining - sz);
}
info->remaining -= sz;
- info->currentsample += sz / (4 * _info->channels);
- _info->readpos += (float)sz / (_info->channels * _info->samplerate * sizeof (float));
+ info->currentsample += n;
+ _info->readpos += (float)n / _info->samplerate;
+ trace ("flac: [2] if=%d, of=%d, n=%d, rem=%d, size=%d\n", n_input_frames, n_output_frames, n, info->remaining, size);
}
if (!size) {
break;
@@ -361,19 +376,36 @@ cflac_read_float32 (DB_fileinfo_t *_info, char *bytes, int size) {
return 0;
}
}
+ int n_output_channels = _info->channels;
+ if (n_output_channels > 2) {
+ n_output_channels = 2;
+ }
int initsize = size;
do {
if (info->remaining) {
- int sz = min (info->remaining, size);
- memcpy (bytes, info->buffer, sz);
- size -= sz;
- bytes += sz;
+ int n_input_frames = info->remaining / sizeof (float) / n_output_channels;
+ int n_output_frames = size / n_output_channels / sizeof (float);
+ int n = min (n_input_frames, n_output_frames);
+
+ float *in = (float *)info->buffer;
+ for (int i = 0; i < n; i++) {
+ *((float *)bytes) = *in;
+ size -= sizeof (float);
+ bytes += sizeof (float);
+ if (n_output_channels == 2) {
+ *((float *)bytes) = *(in+1);
+ size -= sizeof (float);
+ bytes += sizeof (float);
+ }
+ in += n_output_channels;
+ }
+ int sz = n * sizeof (float) * n_output_channels;
if (sz < info->remaining) {
memmove (info->buffer, &info->buffer[sz], info->remaining-sz);
}
info->remaining -= sz;
- info->currentsample += sz / (4 * _info->channels);
- _info->readpos += (float)sz / (_info->channels * _info->samplerate * sizeof (int32_t));
+ info->currentsample += n;
+ _info->readpos += (float)n / _info->samplerate;
}
if (!size) {
break;
@@ -451,6 +483,7 @@ cflac_init_metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__Str
DB_playItem_t *it = info->it;
//it->tracknum = 0;
if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
+ trace ("flac: samplerate=%d, channels=%d\n", metadata->data.stream_info.sample_rate, metadata->data.stream_info.channels);
_info->samplerate = metadata->data.stream_info.sample_rate;
_info->channels = metadata->data.stream_info.channels;
info->totalsamples = metadata->data.stream_info.total_samples;
diff --git a/plugins/mpgmad/mpgmad.c b/plugins/mpgmad/mpgmad.c
index d2355dc5..9a925304 100644
--- a/plugins/mpgmad/mpgmad.c
+++ b/plugins/mpgmad/mpgmad.c
@@ -83,6 +83,11 @@ typedef struct {
int endsample;
int startdelay;
int enddelay;
+ int avg_packetlength;
+ int avg_samplerate;
+ int avg_samples_per_frame;
+ int nframes;
+ int last_comment_update;
} buffer_t;
typedef struct {
@@ -162,6 +167,8 @@ extract_f32 (unsigned char *buf) {
static int
cmp3_scan_stream (buffer_t *buffer, int sample) {
trace ("cmp3_scan_stream %d\n", sample);
+ int initpos = deadbeef->ftell (buffer->file);
+ trace ("filepos: %d\n", initpos);
// if (sample == 0) {
// sample = -1;
// }
@@ -173,13 +180,19 @@ cmp3_scan_stream (buffer_t *buffer, int sample) {
buffer->currentsample = 0;
buffer->skipsamples = 0;
int fsize = 0;
- int avg_packetlength = 0;
- int avg_samplerate = 0;
- int avg_samples_per_frame = 0;
+ int avg_bitrate = 0;
+ int valid_frames = 0;
if (sample <= 0) {
buffer->totalsamples = 0;
- fsize = deadbeef->fgetlength (buffer->file);
+ fsize = deadbeef->fgetlength (buffer->file) - initpos;
+ }
+ if (sample == 0 && buffer->avg_packetlength == 0) {
+ buffer->avg_packetlength = 0;
+ buffer->avg_samplerate = 0;
+ buffer->avg_samples_per_frame = 0;
+ buffer->nframes = 0;
+ buffer->startoffset = initpos;
}
for (;;) {
@@ -190,7 +203,7 @@ cmp3_scan_stream (buffer_t *buffer, int sample) {
break; // eof
}
if (sync != 0xff) {
- trace ("[1]frame %d didn't seek to frame end\n", nframe);
+// trace ("[1]frame %d didn't seek to frame end\n", nframe);
continue; // not an mpeg frame
}
else {
@@ -199,7 +212,7 @@ cmp3_scan_stream (buffer_t *buffer, int sample) {
break; // eof
}
if ((sync >> 5) != 7) {
- trace ("[2]frame %d didn't seek to frame end\n", nframe);
+// trace ("[2]frame %d didn't seek to frame end\n", nframe);
continue;
}
}
@@ -286,11 +299,11 @@ cmp3_scan_stream (buffer_t *buffer, int sample) {
// check if channel/bitrate combination is valid for layer2
if (layer == 2) {
if ((bitrate <= 56 || bitrate == 80) && nchannels != 1) {
- trace ("[1]frame %d channel/bitrate combination is bad\n", nframe);
+ trace ("mpgmad: bad frame %d: layer %d, channels %d, bitrate %d\n", nframe, layer, nchannels, bitrate);
continue; // bad frame
}
if (bitrate >= 224 && nchannels == 1) {
- trace ("[2]frame %d channel/bitrate combination is bad\n", nframe);
+ trace ("mpgmad: bad frame %d: layer %d, channels %d, bitrate %d\n", nframe, layer, nchannels, bitrate);
continue; // bad frame
}
}
@@ -326,6 +339,10 @@ cmp3_scan_stream (buffer_t *buffer, int sample) {
continue;
}
+ valid_frames++;
+ if (nframe < 1000) {
+ trace ("frame %d bitrate %d\n", nframe, bitrate);
+ }
if (sample != 0 || nframe == 0)
{
buffer->version = ver;
@@ -342,7 +359,7 @@ cmp3_scan_stream (buffer_t *buffer, int sample) {
if (sample <= 0 && !got_xing_header)
{
size_t framepos = deadbeef->ftell (buffer->file);
- trace ("trying to read xing header\n");
+// trace ("trying to read xing header at pos %d\n", framepos);
if (ver == 1) {
deadbeef->fseek (buffer->file, 32, SEEK_CUR);
}
@@ -361,7 +378,7 @@ cmp3_scan_stream (buffer_t *buffer, int sample) {
buffer->startoffset = startoffset;
}
- trace ("xing magic: %c%c%c%c\n", magic[0], magic[1], magic[2], magic[3]);
+// trace ("xing magic: %c%c%c%c\n", magic[0], magic[1], magic[2], magic[3]);
if (!strncmp (xing, magic, 4) || !strncmp (info, magic, 4)) {
trace ("xing/info frame found\n");
@@ -396,7 +413,7 @@ cmp3_scan_stream (buffer_t *buffer, int sample) {
if (deadbeef->fread (buf, 1, 4, buffer->file) != 4) {
return -1; // EOF
}
- trace ("tell=%x, %c%c%c%c\n", deadbeef->ftell(buffer->file), buf[0], buf[1], buf[2], buf[3]);
+// trace ("tell=%x, %c%c%c%c\n", deadbeef->ftell(buffer->file), buf[0], buf[1], buf[2], buf[3]);
if (!memcmp (buf, "LAME", 4)) {
trace ("lame header found\n");
deadbeef->fseek (buffer->file, 6, SEEK_CUR);
@@ -444,29 +461,36 @@ cmp3_scan_stream (buffer_t *buffer, int sample) {
}
if (sample == 0) {
// xing header failed, calculate based on file size
- trace ("xing header failed\n");
+// trace ("xing header failed\n");
buffer->samplerate = samplerate;
if (buffer->file->vfs->streaming) {
// only suitable for cbr files, used if streaming
int sz = deadbeef->fgetlength (buffer->file) - buffer->startoffset - buffer->endoffset;
if (sz < 0) {
+ // unable to determine duration
buffer->duration = -1;
buffer->totalsamples = -1;
if (sample == 0) {
- deadbeef->fseek (buffer->file, framepos+packetlength-4, SEEK_SET);
+ deadbeef->fseek (buffer->file, framepos/*+packetlength-4*/, SEEK_SET);
}
return 0;
}
- int nframes = sz / packetlength;
- buffer->duration = nframes * samples_per_frame / samplerate;
- buffer->totalsamples = nframes * samples_per_frame;
+ buffer->nframes = sz / packetlength;
+ buffer->avg_packetlength = packetlength;
+ buffer->avg_samplerate = samplerate;
+ buffer->avg_samples_per_frame = samples_per_frame;
+ buffer->duration = buffer->nframes * samples_per_frame / samplerate;
+ buffer->totalsamples = buffer->nframes * samples_per_frame;
// trace ("bitrate=%d, layer=%d, packetlength=%d, fsize=%d, nframes=%d, samples_per_frame=%d, samplerate=%d, duration=%f, totalsamples=%d\n", bitrate, layer, packetlength, sz, nframes, samples_per_frame, samplerate, buffer->duration, buffer->totalsamples);
if (sample == 0) {
- deadbeef->fseek (buffer->file, framepos+packetlength-4, SEEK_SET);
+ deadbeef->fseek (buffer->file, framepos/*+packetlength-4*/, SEEK_SET);
return 0;
}
}
+ else {
+ deadbeef->fseek (buffer->file, framepos, SEEK_SET);
+ }
}
else {
deadbeef->fseek (buffer->file, framepos+packetlength-4, SEEK_SET);
@@ -479,19 +503,20 @@ cmp3_scan_stream (buffer_t *buffer, int sample) {
return -1;
}
// calculating apx duration based on 1st 100 frames
- avg_packetlength += packetlength;
- avg_samplerate += samplerate;
- avg_samples_per_frame += samples_per_frame;
+ buffer->avg_packetlength += packetlength;
+ buffer->avg_samplerate += samplerate;
+ buffer->avg_samples_per_frame += samples_per_frame;
+ avg_bitrate += bitrate;
if (nframe >= 100) {
- avg_packetlength /= nframe;
- avg_samplerate /= nframe;
- avg_samples_per_frame /= nframe;
-
- trace ("avg_packetlength=%d, avg_samplerate=%d, avg_samples_per_frame=%d\n", avg_packetlength, avg_samplerate, avg_samples_per_frame);
-
- int nframes = fsize / avg_packetlength;
- buffer->duration = nframes * avg_samples_per_frame / avg_samplerate;
- buffer->totalsamples = nframes * avg_samples_per_frame;
+ buffer->avg_packetlength /= valid_frames;
+ buffer->avg_samplerate /= valid_frames;
+ buffer->avg_samples_per_frame /= valid_frames;
+ avg_bitrate /= valid_frames;
+ trace ("valid_frames=%d, avg_bitrate=%d, avg_packetlength=%d, avg_samplerate=%d, avg_samples_per_frame=%d\n", valid_frames, avg_bitrate, buffer->avg_packetlength, buffer->avg_samplerate, buffer->avg_samples_per_frame);
+
+ buffer->nframes = fsize / buffer->avg_packetlength;
+ buffer->duration = buffer->nframes * buffer->avg_samples_per_frame / buffer->avg_samplerate;
+ buffer->totalsamples = buffer->nframes * buffer->avg_samples_per_frame;
return 0;
}
}
@@ -536,6 +561,7 @@ cmp3_init (DB_playItem_t *it) {
if (!info->buffer.file->vfs->streaming) {
int skip = deadbeef->junk_get_leading_size (info->buffer.file);
if (skip > 0) {
+ trace ("mpgmad: skipping %d bytes of junk\n", skip);
deadbeef->fseek (info->buffer.file, skip, SEEK_SET);
}
cmp3_scan_stream (&info->buffer, -1); // scan entire stream, calc duration
@@ -813,6 +839,30 @@ cmp3_stream_frame (mpgmad_info_t *info) {
deadbeef->streamer_set_bitrate (info->frame.header.bitrate/1000);
break;
}
+
+ if (!eof) {
+ if (info->buffer.file->vfs->streaming && info->buffer.currentsample - info->buffer.last_comment_update > 5 * info->info.samplerate) {
+ int idx = deadbeef->pl_get_idx_of (info->buffer.it);
+ if (idx >= 0) {
+ info->buffer.last_comment_update = info->buffer.currentsample;
+ const char *vfs_tit = deadbeef->fget_content_name (info->buffer.file);
+ if (vfs_tit) {
+ const char *cs = deadbeef->junk_detect_charset (vfs_tit);
+ if (cs) {
+ char out[1024];
+ deadbeef->junk_recode (vfs_tit, strlen (vfs_tit), out, sizeof (out), cs);
+ deadbeef->pl_replace_meta (info->buffer.it, "title", out);
+ deadbeef->sendmessage (M_TRACKCHANGED, 0, idx, 0);
+ }
+ else {
+ deadbeef->pl_replace_meta (info->buffer.it, "title", vfs_tit);
+ deadbeef->sendmessage (M_TRACKCHANGED, 0, idx, 0);
+ }
+ }
+ }
+ }
+ }
+
return eof;
}
@@ -896,15 +946,28 @@ cmp3_seek_sample (DB_fileinfo_t *_info, int sample) {
}
if (info->buffer.file->vfs->streaming) {
- if (info->buffer.totalsamples > 0) {
- // approximation
- int64_t l = deadbeef->fgetlength (info->buffer.file);
- l = l * sample / info->buffer.totalsamples;
- int r = deadbeef->fseek (info->buffer.file, l, SEEK_SET);
+ if (info->buffer.totalsamples > 0 && info->buffer.avg_samples_per_frame && info->buffer.avg_packetlength) { // that means seekable remote stream, like podcast
+ trace ("seeking is possible!\n");
+ // get length excluding id3v2
+ int64_t l = deadbeef->fgetlength (info->buffer.file) - info->buffer.startoffset;
+
+ int r;
+
+ // seek to beginning of the frame
+ int frm = sample / info->buffer.avg_samples_per_frame;
+ r = deadbeef->fseek (info->buffer.file, frm * info->buffer.avg_packetlength, SEEK_SET);
+
+// l = l * sample / buffer.totalsamples;
+// r = deadbeef->fseek (buffer.file, l, SEEK_SET);
+
if (!r) {
+ trace ("seek successful!\n");
+ info->buffer.skipsamples = sample - frm * info->buffer.avg_samples_per_frame;
+
info->buffer.currentsample = sample;
_info->readpos = (float)(info->buffer.currentsample - info->buffer.startsample) / info->buffer.samplerate;
+ // reset mad
mad_synth_finish (&info->synth);
mad_frame_finish (&info->frame);
mad_stream_finish (&info->stream);
@@ -916,8 +979,10 @@ cmp3_seek_sample (DB_fileinfo_t *_info, int sample) {
return 0;
}
+ trace ("seek failed!\n");
return -1;
}
+ trace ("seek is impossible (avg_samples_per_frame=%d, avg_packetlength=%d)!\n", buffer.avg_samples_per_frame, buffer.avg_packetlength);
return 0;
}
diff --git a/plugins/vfs_curl/vfs_curl.c b/plugins/vfs_curl/vfs_curl.c
index 127a5a4e..972a5da9 100644
--- a/plugins/vfs_curl/vfs_curl.c
+++ b/plugins/vfs_curl/vfs_curl.c
@@ -24,8 +24,8 @@
#include <curl/curlver.h>
#include "../../deadbeef.h"
-//#define trace(...) { fprintf(stderr, __VA_ARGS__); }
-#define trace(fmt,...)
+#define trace(...) { fprintf(stderr, __VA_ARGS__); }
+//#define trace(fmt,...)
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
@@ -35,6 +35,8 @@ static DB_functions_t *deadbeef;
#define BUFFER_SIZE (0x10000)
#define BUFFER_MASK 0xffff
+#define TIMEOUT 10 // in seconds
+
#define STATUS_INITIAL 0
#define STATUS_STARTING 1
#define STATUS_READING 2
@@ -56,9 +58,11 @@ typedef struct {
char *content_type;
char *content_name;
char *content_genre;
+ CURL *curl;
+ struct timeval last_read_time;
uint8_t status;
-// int icy_metaint;
-// int wait_meta;
+ int icy_metaint;
+ int wait_meta;
// flags (bitfields to save some space)
unsigned seektoend : 1; // indicates that next tell must return length
unsigned gotheader : 1; // tells that all headers (including ICY) were processed (to start reading body)
@@ -77,10 +81,96 @@ static size_t
http_content_header_handler (void *ptr, size_t size, size_t nmemb, void *stream);
static size_t
+http_curl_write_wrapper (HTTP_FILE *fp, void *ptr, size_t size) {
+ size_t avail = size;
+ while (avail > 0) {
+ if (vfs_curl_abort) {
+ break;
+ }
+ deadbeef->mutex_lock (fp->mutex);
+ if (fp->status == STATUS_SEEK) {
+ trace ("vfs_curl seek request, aborting current request\n");
+ deadbeef->mutex_unlock (fp->mutex);
+ return 0;
+ }
+ if (fp->status == STATUS_ABORTED) {
+ trace ("vfs_curl STATUS_ABORTED in the middle of packet\n");
+ deadbeef->mutex_unlock (fp->mutex);
+ break;
+ }
+ int sz = BUFFER_SIZE/2 - fp->remaining; // number of bytes free in buffer
+ // don't allow to fill more than half -- used for seeking backwards
+
+ if (sz > 5000) { // wait until there are at least 5k bytes free
+ int cp = min (avail, sz);
+ int writepos = (fp->pos + fp->remaining) & BUFFER_MASK;
+ // copy 1st portion (before end of buffer
+ int part1 = BUFFER_SIZE - writepos;
+ // may not be more than total
+ part1 = min (part1, cp);
+ memcpy (fp->buffer+writepos, ptr, part1);
+ ptr += part1;
+ avail -= part1;
+ fp->remaining += part1;
+ cp -= part1;
+ if (cp > 0) {
+ memcpy (fp->buffer, ptr, cp);
+ ptr += cp;
+ avail -= cp;
+ fp->remaining += cp;
+ }
+ }
+ deadbeef->mutex_unlock (fp->mutex);
+ usleep (3000);
+ }
+ return size - avail;
+}
+
+int
+http_parse_shoutcast_meta (HTTP_FILE *fp, const char *meta, int size) {
+ trace ("reading %d bytes of metadata\n", size);
+ trace ("%s\n", meta);
+ const char *e = meta + size;
+ const char strtitle[] = "StreamTitle='";
+ char title[256] = "";
+ while (meta < e) {
+ if (!memcmp (meta, strtitle, sizeof (strtitle)-1)) {
+ trace ("extracting streamtitle\n");
+ meta += sizeof (strtitle)-1;
+ const char *substr_end = meta;
+ while (substr_end < e-1 && (*substr_end != '\'' || *(substr_end+1) != ';')) {
+ substr_end++;
+ }
+ if (substr_end >= e) {
+ return -1; // end of string not found
+ }
+ int s = substr_end - meta;
+ s = min (sizeof (title)-1, s);
+ memcpy (title, meta, s);
+ title[s] = 0;
+ trace ("got stream title: %s\n", title);
+ if (fp->content_name) {
+ free (fp->content_name);
+ }
+ fp->content_name = strdup (title);
+ return 0;
+ }
+ while (meta < e && *meta != ';') {
+ meta++;
+ }
+ if (meta < e) {
+ meta++;
+ }
+ }
+ return -1;
+}
+
+static size_t
http_curl_write (void *ptr, size_t size, size_t nmemb, void *stream) {
-// trace ("http_curl_write %d bytes\n", size * nmemb);
int avail = size * nmemb;
HTTP_FILE *fp = (HTTP_FILE *)stream;
+ trace ("http_curl_write %d bytes, wait_meta=%d\n", size * nmemb, fp->wait_meta);
+ gettimeofday (&fp->last_read_time, NULL);
if (fp->status == STATUS_ABORTED) {
trace ("vfs_curl STATUS_ABORTED at start of packet\n");
return 0;
@@ -100,29 +190,25 @@ http_curl_write (void *ptr, size_t size, size_t nmemb, void *stream) {
fp->gotheader = 1;
}
else {
- trace ("parsing icy headers:\n%s\n", ptr);
+// trace ("parsing icy headers:\n%s\n", ptr);
fp->nheaderpackets++;
- http_content_header_handler (ptr, size, nmemb, stream);
- if (fp->gotheader) {
- fp->gotheader = 0; // don't reset icy header
- }
- uint8_t *p = ptr;
- int i;
- for (i = 0; i < avail-3; i++) {
- const char end[4] = { 0x0d, 0x0a, 0x0d, 0x0a };
- if (!memcmp (p, end, 4)) {
- trace ("icy headers end\n");
- fp->gotheader = 1;
- break;
+ avail = http_content_header_handler (ptr, size, nmemb, stream);
+ if (avail == size * nmemb) {
+ if (fp->gotheader) {
+ fp->gotheader = 0; // don't reset icy header
}
- p++;
}
- avail = 0;
+ else {
+ fp->gotheader = 1;
+ }
}
}
else {
fp->gotheader = 1;
}
+ if (!avail) {
+ return nmemb*size;
+ }
}
deadbeef->mutex_lock (fp->mutex);
@@ -130,69 +216,43 @@ http_curl_write (void *ptr, size_t size, size_t nmemb, void *stream) {
fp->status = STATUS_READING;
}
deadbeef->mutex_unlock (fp->mutex);
- while (avail > 0) {
- if (vfs_curl_abort) {
- break;
- }
- deadbeef->mutex_lock (fp->mutex);
- if (fp->status == STATUS_SEEK) {
- trace ("vfs_curl seek request, aborting current request\n");
- deadbeef->mutex_unlock (fp->mutex);
- return 0;
- }
- if (fp->status == STATUS_ABORTED) {
- trace ("vfs_curl STATUS_ABORTED in the middle of packet\n");
- deadbeef->mutex_unlock (fp->mutex);
- break;
- }
-#if 0
- if (fp->wait_meta == 0) {
- char sz;
- memcpy (&sz, ptr, 1);
- printf ("reading %d bytes of metadata, seekpos:%d!\n", (int)sz*4, fp->pos);
- ptr += 16 * sz;
- avail -= 16 * sz + 1;
- printf ("avail=%d!\n", avail);
- fp->wait_meta = fp->icy_metaint;
- }
-#endif
- int sz = BUFFER_SIZE/2 - fp->remaining; // number of bytes free in buffer
- // don't allow to fill more than half -- used for seeking backwards
- if (sz > 5000) { // wait until there are at least 5k bytes free
- int cp = min (avail, sz);
-#if 0
- if (fp->wait_meta - cp <= 0) {
- printf ("cp=%d->%d\n", cp, fp->wait_meta);
- cp = fp->wait_meta;
+ if (fp->icy_metaint > 0) {
+ while (fp->wait_meta < avail) {
+ trace ("http_curl_write_wrapper [1] %d\n", fp->wait_meta);
+ size_t res1 = http_curl_write_wrapper (fp, ptr, fp->wait_meta);
+ if (res1 != fp->wait_meta) {
+ return 0;
}
- fp->wait_meta -= cp;
-#endif
-
- int writepos = (fp->pos + fp->remaining) & BUFFER_MASK;
- // copy 1st portion (before end of buffer
- int part1 = BUFFER_SIZE - writepos;
- // may not be more than total
- part1 = min (part1, cp);
- //trace ("part1=%d\n", part1);
-// trace ("writepos=%d, remaining=%d, avail=%d, free=%d, writing=%d, part1=%d, part2=%d\n", writepos, fp->remaining, avail, sz, cp, part1, cp-part1);
- memcpy (fp->buffer+writepos, ptr, part1);
- ptr += part1;
- avail -= part1;
- fp->remaining += part1;
- cp -= part1;
- if (cp > 0) {
- memcpy (fp->buffer, ptr, cp);
- ptr += cp;
- avail -= cp;
- fp->remaining += cp;
+ avail -= res1;
+ ptr += res1;
+ uint32_t sz = (uint32_t)(*((uint8_t *)ptr)) * 16;
+ ptr ++;
+ if (sz > 0) {
+ if (http_parse_shoutcast_meta (fp, ptr, sz) < 0) {
+ trace ("vfs_curl: got invalid icy metadata block\n");
+ fp->remaining = 0;
+ fp->status = STATUS_SEEK;
+ return 0;
+ }
}
+ avail -= sz + 1;
+ fp->wait_meta = fp->icy_metaint;
+ trace ("avail: %d\n", avail);
}
- deadbeef->mutex_unlock (fp->mutex);
- usleep (3000);
}
-// trace ("returning %d\n", nmemb * size - avail);
+ if (avail < 0) {
+ trace ("vfs_curl: something bad happened in metadata parser. can't continue streaming.\n");
+ return 0;
+ }
+
+ if (avail) {
+ trace ("http_curl_write_wrapper [2] %d\n", avail);
+ size_t res = http_curl_write_wrapper (fp, ptr, avail);
+ avail -= res;
+ fp->wait_meta -= res;
+ }
return nmemb * size - avail;
}
@@ -247,10 +307,6 @@ parse_header (const uint8_t *p, const uint8_t *e, uint8_t *key, int keysize, uin
memcpy (value, p, sz);
value[sz] = 0;
- // skip linebreaks
- while (v < e && (*v == 0x0d || *v == 0x0a)) {
- v++;
- }
return v;
}
@@ -264,6 +320,16 @@ http_content_header_handler (void *ptr, size_t size, size_t nmemb, void *stream)
uint8_t key[256];
uint8_t value[256];
while (p < end) {
+ if (p < end - 4) {
+ if (!memcmp (p, "\r\n\r\n", 4)) {
+ p += 4;
+ return size * nmemb - (size_t)(p-(const uint8_t *)ptr);
+ }
+ }
+ // skip linebreaks
+ while (p < end && (*p == 0x0d || *p == 0x0a)) {
+ p++;
+ }
p = parse_header (p, end, key, sizeof (key), value, sizeof (value));
trace ("%skey=%s value=%s\n", fp->icyheader ? "[icy] " : "", key, value);
if (!strcasecmp (key, "Content-Type")) {
@@ -287,11 +353,11 @@ http_content_header_handler (void *ptr, size_t size, size_t nmemb, void *stream)
}
fp->content_genre = strdup (value);
}
-// else if (!strcasecmp (key, "icy-metaint")) {
-// //printf ("icy-metaint: %d\n", atoi (value));
-// fp->icy_metaint = atoi (value);
-// fp->wait_meta = fp->icy_metaint;
-// }
+ else if (!strcasecmp (key, "icy-metaint")) {
+ //printf ("icy-metaint: %d\n", atoi (value));
+ fp->icy_metaint = atoi (value);
+ fp->wait_meta = fp->icy_metaint;
+ }
}
if (!fp->icyheader) {
fp->gotsomeheader = 1;
@@ -302,7 +368,19 @@ http_content_header_handler (void *ptr, size_t size, size_t nmemb, void *stream)
static int
http_curl_control (void *stream, double dltotal, double dlnow, double ultotal, double ulnow) {
HTTP_FILE *fp = (HTTP_FILE *)stream;
- trace ("http_curl_control, status = %d\n", fp ? fp->status : -1);
+
+ struct timeval tm;
+ gettimeofday (&tm, NULL);
+ float sec = tm.tv_sec - fp->last_read_time.tv_sec;
+ long response;
+ CURLcode code = curl_easy_getinfo (fp->curl, CURLINFO_RESPONSE_CODE, &response);
+ trace ("http_curl_control: status = %d, response = %d, interval: %f seconds\n", fp ? fp->status : -1, response, sec);
+ if (fp->status == STATUS_READING && sec > TIMEOUT) {
+ trace ("http_curl_control: timed out, restarting read\n");
+ memcpy (&fp->last_read_time, &tm, sizeof (struct timeval));
+ fp->remaining = 0;
+ fp->status = STATUS_SEEK;
+ }
assert (stream);
if (fp->status == STATUS_ABORTED) {
trace ("vfs_curl STATUS_ABORTED in progress callback\n");
@@ -322,12 +400,13 @@ http_thread_func (void *ctx) {
curl = curl_easy_init ();
fp->length = -1;
fp->status = STATUS_INITIAL;
+ fp->curl = curl;
int status;
trace ("vfs_curl: started loading data\n");
for (;;) {
-// struct curl_slist *headers = NULL;
+ struct curl_slist *headers = NULL;
curl_easy_reset (curl);
curl_easy_setopt (curl, CURLOPT_URL, fp->url);
curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 1);
@@ -344,8 +423,8 @@ http_thread_func (void *ctx) {
// enable up to 10 redirects
curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt (curl, CURLOPT_MAXREDIRS, 10);
-// headers = curl_slist_append (headers, "Icy-Metadata:1");
-// curl_easy_setopt (curl, CURLOPT_HTTPHEADER, headers);
+ headers = curl_slist_append (headers, "Icy-Metadata:1");
+ curl_easy_setopt (curl, CURLOPT_HTTPHEADER, headers);
if (fp->pos > 0) {
curl_easy_setopt (curl, CURLOPT_RESUME_FROM, fp->pos);
}
@@ -392,11 +471,37 @@ http_thread_func (void *ctx) {
deadbeef->mutex_unlock (fp->mutex);
break;
}
- fp->status = STATUS_INITIAL;
- trace ("seeking to %d\n", fp->pos);
+ else {
+ fp->skipbytes = 0;
+ fp->status = STATUS_INITIAL;
+ trace ("seeking to %d\n", fp->pos);
+ if (fp->length < 0) {
+ // icy -- need full restart
+ fp->pos = 0;
+ if (fp->content_type) {
+ free (fp->content_type);
+ fp->content_type = NULL;
+ }
+ if (fp->content_name) {
+ free (fp->content_name);
+ fp->content_name = NULL;
+ }
+ if (fp->content_genre) {
+ free (fp->content_genre);
+ fp->content_genre = NULL;
+ }
+ fp->seektoend = 0;
+ fp->gotheader = 0;
+ fp->icyheader = 0;
+ fp->gotsomeheader = 0;
+ fp->wait_meta = 0;
+ fp->icy_metaint = 0;
+ }
+ }
deadbeef->mutex_unlock (fp->mutex);
-// curl_slist_free_all (headers);
+ curl_slist_free_all (headers);
}
+ fp->curl = NULL;
curl_easy_cleanup (curl);
deadbeef->mutex_lock (fp->mutex);
@@ -465,8 +570,22 @@ http_read (void *ptr, size_t size, size_t nmemb, DB_FILE *stream) {
{
// wait until data is available
while ((fp->remaining == 0 || fp->skipbytes > 0) && fp->status != STATUS_FINISHED && !vfs_curl_abort) {
- trace ("vfs_curl: readwait..\n");
+// trace ("vfs_curl: readwait..\n");
deadbeef->mutex_lock (fp->mutex);
+ if (fp->status == STATUS_READING) {
+ struct timeval tm;
+ gettimeofday (&tm, NULL);
+ float sec = tm.tv_sec - fp->last_read_time.tv_sec;
+ if (sec > TIMEOUT) {
+ trace ("http_read: timed out, restarting read\n");
+ memcpy (&fp->last_read_time, &tm, sizeof (struct timeval));
+ fp->remaining = 0;
+ fp->status = STATUS_SEEK;
+ deadbeef->mutex_unlock (fp->mutex);
+ deadbeef->streamer_reset (1);
+ continue;
+ }
+ }
int skip = min (fp->remaining, fp->skipbytes);
if (skip > 0) {
// trace ("skipping %d bytes\n");
@@ -634,6 +753,7 @@ http_get_content_name (DB_FILE *stream) {
return NULL;
}
if (fp->gotheader) {
+ trace ("returning %s\n", fp->content_name);
return fp->content_name;
}
if (!fp->tid) {