diff options
author | Alexey Yakovenko <wakeroid@gmail.com> | 2010-01-31 20:26:17 +0100 |
---|---|---|
committer | Alexey Yakovenko <wakeroid@gmail.com> | 2010-01-31 20:26:17 +0100 |
commit | 6e6dc88fc61f4a84269a592c76c2b6bd0417bf4a (patch) | |
tree | 8b5b1fe103a9a4e93a680493d9f1dd2fcb71ded2 /plugins | |
parent | 9460844e2a1598dfe24a72dd2a283d02b57caddc (diff) | |
parent | 893655b27084db6399b82069a5dcb56d6e98fbfa (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.c | 10 | ||||
-rw-r--r-- | plugins/flac/flac.c | 77 | ||||
-rw-r--r-- | plugins/mpgmad/mpgmad.c | 133 | ||||
-rw-r--r-- | plugins/vfs_curl/vfs_curl.c | 308 |
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) { |