summaryrefslogtreecommitdiff
path: root/streamer.c
diff options
context:
space:
mode:
Diffstat (limited to 'streamer.c')
-rw-r--r--streamer.c370
1 files changed, 293 insertions, 77 deletions
diff --git a/streamer.c b/streamer.c
index 8a8b35f3..2b05bdde 100644
--- a/streamer.c
+++ b/streamer.c
@@ -82,11 +82,14 @@ static float playpos = 0; // play position of current song
static int avg_bitrate = -1; // avg bitrate of current song
static int last_bitrate = -1; // last bitrate of current song
+// copies of current playing and streaming tracks
playItem_t str_playing_song;
playItem_t str_streaming_song;
// remember pointers to original instances of playitems
static playItem_t *orig_playing_song;
static playItem_t *orig_streaming_song;
+// current decoder
+static DB_fileinfo_t *str_current_decoder;
static int streamer_buffering;
@@ -118,7 +121,7 @@ streamer_song_removed_notify (playItem_t *it) {
// queue new next song for streaming
if (bytes_until_next_song > 0) {
streambuffer_fill = bytes_until_next_song;
- pl_nextsong (0);
+ streamer_move_nextsong (0);
}
}
}
@@ -133,21 +136,24 @@ streamer_set_current (playItem_t *it) {
if (to != -1) {
messagepump_push (M_TRACKCHANGED, 0, to, 0);
}
+#if 0
+ // this breaks redraw
if (!orig_playing_song || p_isstopped ()) {
- playlist_current_ptr = it;
- //trace ("from=%d, to=%d\n", from, to);
- //messagepump_push (M_SONGCHANGED, 0, from, to);
+ orig_playing_song = it;
}
+#endif
trace ("streamer_set_current %p, buns=%d\n", it);
- if(str_streaming_song.decoder) {
- str_streaming_song.decoder->free ();
- pl_item_free (&str_streaming_song);
+ if(str_current_decoder) {
+ str_current_decoder->plugin->free (str_current_decoder);
+ str_current_decoder = NULL;
+ pl_item_unref (&str_streaming_song);
}
orig_streaming_song = it;
if (!it) {
goto success;
}
- if (!it->decoder && it->filetype && !strcmp (it->filetype, "content")) {
+ DB_decoder_t *dec = NULL;
+ if (!it->decoder_id && it->filetype && !strcmp (it->filetype, "content")) {
// try to get content-type
DB_FILE *fp = streamer_file = vfs_fopen (it->fname);
const char *plug = NULL;
@@ -166,34 +172,31 @@ streamer_set_current (playItem_t *it) {
vfs_fclose (fp);
}
if (plug) {
- DB_decoder_t **decoders = plug_get_decoder_list ();
- // match by decoder
- for (int i = 0; decoders[i]; i++) {
- if (!strcmp (decoders[i]->id, plug)) {
- it->decoder = decoders[i];
- it->filetype = decoders[i]->filetypes[0];
- }
+ dec = plug_get_decoder_for_id (plug);
+ if (dec) {
+ it->decoder_id = plug_get_decoder_id (plug);
+ it->filetype = dec->filetypes[0];
}
}
}
- if (it->decoder) {
- streamer_lock ();
- streamer_unlock ();
- int ret = it->decoder->init (DB_PLAYITEM (it));
- streamer_lock ();
- streamer_unlock ();
+ else if (it->decoder_id) {
+ dec = plug_get_decoder_for_id (it->decoder_id);
+ }
+ if (dec) {
+ str_current_decoder = dec->init (DB_PLAYITEM (it));
+ trace ("str: acquired decoder ptr %p\n", str_current_decoder);
pl_item_copy (&str_streaming_song, it);
- if (ret < 0) {
- trace ("decoder->init returned %d\n", ret);
+ if (!str_current_decoder) {
+ trace ("decoder->init returned NULL\n");
trace ("orig_playing_song = %p\n", orig_playing_song);
- if (playlist_current_ptr == it) {
- playlist_current_ptr = NULL;
+ if (orig_playing_song == it) {
+ orig_playing_song = NULL;
messagepump_push (M_TRACKCHANGED, 0, to, 0);
}
- return ret;
+ return -1;
}
else {
- trace ("bps=%d, channels=%d, samplerate=%d\n", it->decoder->info.bps, it->decoder->info.channels, it->decoder->info.samplerate);
+ trace ("bps=%d, channels=%d, samplerate=%d\n", str_current_decoder->bps, str_current_decoder->channels, str_current_decoder->samplerate);
}
streamer_reset (0); // reset SRC
}
@@ -241,9 +244,6 @@ streamer_set_nextsong (int song, int pstate) {
// no sense to wait until end of previous song, reset buffer
bytes_until_next_song = 0;
playpos = 0;
- // try to interrupt file operation
- streamer_lock ();
- streamer_unlock ();
}
}
@@ -283,8 +283,8 @@ streamer_thread (void *ctx) {
if (!try) { // track is not in playlist
trace ("track #%d is not in playlist; stopping playback\n", sng);
p_stop ();
- pl_item_free (&str_playing_song);
- pl_item_free (&str_streaming_song);
+ pl_item_unref (&str_playing_song);
+ pl_item_unref (&str_streaming_song);
orig_playing_song = NULL;
orig_streaming_song = NULL;
messagepump_push (M_SONGCHANGED, 0, -1, -1);
@@ -298,8 +298,8 @@ streamer_thread (void *ctx) {
badsong = sng;
}
// try jump to next song
- pl_nextsong (0);
- trace ("pl_nextsong switched to track %d\n", nextsong);
+ streamer_move_nextsong (0);
+ trace ("streamer_move_nextsong switched to track %d\n", nextsong);
usleep (50000);
continue;
}
@@ -327,12 +327,12 @@ streamer_thread (void *ctx) {
trace ("nextsong=-2\n");
nextsong = -1;
p_stop ();
- if (str_playing_song.decoder) {
+ if (str_current_decoder) {
trace ("sending songfinished to plugins [1]\n");
plug_trigger_event (DB_EV_SONGFINISHED, 0);
}
streamer_set_current (NULL);
- pl_item_free (&str_playing_song);
+ pl_item_unref (&str_playing_song);
orig_playing_song = NULL;
messagepump_push (M_SONGCHANGED, 0, from, -1);
continue;
@@ -358,12 +358,12 @@ streamer_thread (void *ctx) {
messagepump_push (M_SONGCHANGED, 0, from, to);
bytes_until_next_song = -1;
// plugin will get pointer to str_playing_song
- if (str_playing_song.decoder) {
+ if (str_current_decoder) {
trace ("sending songfinished to plugins [2]\n");
plug_trigger_event (DB_EV_SONGFINISHED, 0);
}
// free old copy of playing
- pl_item_free (&str_playing_song);
+ pl_item_unref (&str_playing_song);
// copy streaming into playing
pl_item_copy (&str_playing_song, &str_streaming_song);
last_bitrate = -1;
@@ -374,7 +374,6 @@ streamer_thread (void *ctx) {
orig_playing_song->started_timestamp = time (NULL);
str_playing_song.started_timestamp = time (NULL);
}
- playlist_current_ptr = orig_playing_song;
// that is needed for playlist drawing
// plugin will get pointer to new str_playing_song
trace ("sending songstarted to plugins\ncurrent playtrack: %s\n", str_playing_song.fname);
@@ -386,17 +385,16 @@ streamer_thread (void *ctx) {
// don't switch if unchanged
int prevtrack_samplerate = p_get_rate ();
- if (prevtrack_samplerate != str_playing_song.decoder->info.samplerate) {
- int newrate = plug_get_output ()->change_rate (str_playing_song.decoder->info.samplerate);
+ if (prevtrack_samplerate != str_current_decoder->samplerate) {
+ int newrate = plug_get_output ()->change_rate (str_current_decoder->samplerate);
if (newrate != prevtrack_samplerate) {
// restart streaming of current track
trace ("streamer: output samplerate changed from %d to %d; restarting track\n", prevtrack_samplerate, newrate);
- str_streaming_song.decoder->free ();
- str_streaming_song.decoder->init (DB_PLAYITEM (orig_streaming_song));
+ str_current_decoder->plugin->seek (str_current_decoder, 0);
bytes_until_next_song = -1;
streamer_buffering = 1;
streamer_reset (1);
- prevtrack_samplerate = str_playing_song.decoder->info.samplerate;
+ prevtrack_samplerate = str_current_decoder->samplerate;
}
}
@@ -419,9 +417,10 @@ streamer_thread (void *ctx) {
if (orig_playing_song != orig_streaming_song) {
trace ("streamer already switched to next track\n");
// restart playing from new position
- if(str_streaming_song.decoder) {
- str_streaming_song.decoder->free ();
- pl_item_free (&str_streaming_song);
+ if(str_current_decoder) {
+ str_current_decoder->plugin->free (str_current_decoder);
+ str_current_decoder = NULL;
+ pl_item_unref (&str_streaming_song);
}
orig_streaming_song = orig_playing_song;
pl_item_copy (&str_streaming_song, orig_streaming_song);
@@ -431,14 +430,18 @@ streamer_thread (void *ctx) {
if (trk != -1) {
messagepump_push (M_TRACKCHANGED, 0, trk, 0);
}
- int ret = str_streaming_song.decoder->init (DB_PLAYITEM (orig_streaming_song));
- if (ret < 0) {
+ DB_decoder_t *plug = plug_get_decoder_for_id (orig_streaming_song->decoder_id);
+ if (plug) {
+ str_current_decoder = plug->init (DB_PLAYITEM (orig_streaming_song));
+ }
+ if (!plug || !str_current_decoder) {
+ streamer_buffering = 0;
if (trk != -1) {
messagepump_push (M_TRACKCHANGED, 0, trk, 0);
}
trace ("failed to restart prev track on seek, trying to jump to next track\n");
- pl_nextsong (0);
- trace ("pl_nextsong switched to track %d\n", nextsong);
+ streamer_move_nextsong (0);
+ trace ("streamer_move_nextsong switched to track %d\n", nextsong);
usleep (50000);
continue;
}
@@ -449,15 +452,15 @@ streamer_thread (void *ctx) {
if (trk != -1) {
messagepump_push (M_TRACKCHANGED, 0, trk, 0);
}
- if (str_playing_song.decoder && str_playing_song._duration > 0) {
+ if (str_current_decoder && str_playing_song._duration > 0) {
streamer_lock ();
streambuffer_fill = 0;
streambuffer_pos = 0;
codec_lock ();
codecleft = 0;
codec_unlock ();
- if (str_playing_song.decoder->seek (pos) >= 0) {
- playpos = str_playing_song.decoder->info.readpos;
+ if (str_current_decoder->plugin->seek (str_current_decoder, pos) >= 0) {
+ playpos = str_current_decoder->readpos;
}
last_bitrate = -1;
avg_bitrate = -1;
@@ -509,11 +512,12 @@ streamer_thread (void *ctx) {
}
// stop streaming song
- if(str_streaming_song.decoder) {
- str_streaming_song.decoder->free ();
+ if(str_current_decoder) {
+ str_current_decoder->plugin->free (str_current_decoder);
+ str_current_decoder = NULL;
}
- pl_item_free (&str_streaming_song);
- pl_item_free (&str_playing_song);
+ pl_item_unref (&str_streaming_song);
+ pl_item_unref (&str_playing_song);
if (src) {
src_delete (src);
src = NULL;
@@ -705,24 +709,28 @@ streamer_read_async (char *bytes, int size) {
for (;;) {
int bytesread = 0;
codec_lock ();
- DB_decoder_t *decoder = str_streaming_song.decoder;
+ if (!str_current_decoder) {
+ codec_unlock ();
+ break;
+ }
+ DB_decoder_t *decoder = str_current_decoder->plugin;
if (!decoder) {
// means there's nothing left to stream, so just do nothing
codec_unlock ();
break;
}
- if (decoder->info.samplerate != -1) {
- int nchannels = decoder->info.channels;
- int samplerate = decoder->info.samplerate;
- if (decoder->info.samplerate == p_get_rate ()) {
+ if (str_current_decoder->samplerate != -1) {
+ int nchannels = str_current_decoder->channels;
+ int samplerate = str_current_decoder->samplerate;
+ if (str_current_decoder->samplerate == p_get_rate ()) {
// samplerate match
- if (decoder->info.channels == 2) {
- bytesread = decoder->read_int16 (bytes, size);
+ if (str_current_decoder->channels == 2) {
+ bytesread = decoder->read_int16 (str_current_decoder, bytes, size);
apply_replay_gain_int16 (&str_streaming_song, bytes, size);
codec_unlock ();
}
else {
- bytesread = decoder->read_int16 (g_readbuffer, size>>1);
+ bytesread = decoder->read_int16 (str_current_decoder, g_readbuffer, size>>1);
apply_replay_gain_int16 (&str_streaming_song, g_readbuffer, size>>1);
mono_int16_to_stereo_int16 ((int16_t*)g_readbuffer, (int16_t*)bytes, size>>2);
bytesread *= 2;
@@ -745,7 +753,7 @@ streamer_read_async (char *bytes, int size) {
trace ("input buffer overflow\n");
nbytes = INPUT_BUFFER_SIZE;
}
- bytesread = decoder->read_int16 (g_readbuffer, nbytes);
+ bytesread = decoder->read_int16 (str_current_decoder, g_readbuffer, nbytes);
apply_replay_gain_int16 (&str_streaming_song, g_readbuffer, nbytes);
}
else {
@@ -772,7 +780,7 @@ streamer_read_async (char *bytes, int size) {
float *fbuffer = g_fbuffer + codecleft*2;
if (nchannels == 1) {
codec_lock ();
- bytesread = decoder->read_float32 (g_readbuffer, nbytes*2);
+ bytesread = decoder->read_float32 (str_current_decoder, g_readbuffer, nbytes*2);
codec_unlock ();
apply_replay_gain_float32 (&str_streaming_song, g_readbuffer, nbytes*2);
nsamples = bytesread / (samplesize * nchannels) + codecleft;
@@ -780,7 +788,7 @@ streamer_read_async (char *bytes, int size) {
}
else {
codec_lock ();
- bytesread = decoder->read_float32 ((char *)fbuffer, nbytes*2);
+ bytesread = decoder->read_float32 (str_current_decoder, (char *)fbuffer, nbytes*2);
codec_unlock ();
apply_replay_gain_float32 (&str_streaming_song, (char *)fbuffer, nbytes*2);
nsamples = bytesread / (samplesize * nchannels) + codecleft;
@@ -833,7 +841,7 @@ streamer_read_async (char *bytes, int size) {
streamer_set_nextsong (-2, 1);
}
else {
- pl_nextsong (0);
+ streamer_move_nextsong (0);
}
}
break;
@@ -866,11 +874,9 @@ streamer_read (char *bytes, int size) {
streambuffer_fill -= sz;
playpos += (float)sz/p_get_rate ()/4.f;
str_playing_song.playtime += (float)sz/p_get_rate ()/4.f;
- if (playlist_current_ptr) {
- str_playing_song.filetype = playlist_current_ptr->filetype;
- }
- if (playlist_current_ptr) {
- playlist_current_ptr->playtime = str_playing_song.playtime;
+ if (orig_playing_song) {
+ str_playing_song.filetype = orig_playing_song->filetype;
+ orig_playing_song->playtime = str_playing_song.playtime;
}
if (bytes_until_next_song > 0) {
bytes_until_next_song -= sz;
@@ -963,12 +969,13 @@ streamer_configchanged (void) {
void
streamer_play_current_track (void) {
+ playlist_t *plt = plt_get_curr_ptr ();
if (p_ispaused ()) {
// unpause currently paused track
p_unpause ();
plug_trigger_event_paused (0);
}
- else if (playlist_current_row[PL_MAIN] != -1) {
+ else if (plt->current_row[PL_MAIN] != -1) {
// play currently selected track
p_stop ();
// get next song in queue
@@ -979,7 +986,7 @@ streamer_play_current_track (void) {
pl_playqueue_pop ();
}
else {
- idx = playlist_current_row[PL_MAIN];
+ idx = plt->current_row[PL_MAIN];
}
streamer_set_nextsong (idx, 1);
@@ -991,3 +998,212 @@ streamer_play_current_track (void) {
}
}
+int
+streamer_move_prevsong (void) {
+ playlist_t *plt = plt_get_curr_ptr ();
+ pl_playqueue_clear ();
+ if (!plt->head[PL_MAIN]) {
+ streamer_set_nextsong (-2, 1);
+ return 0;
+ }
+ int pl_order = conf_get_int ("playback.order", 0);
+ int pl_loop_mode = conf_get_int ("playback.loop", 0);
+ if (pl_order == PLAYBACK_ORDER_SHUFFLE) { // shuffle
+ if (!orig_playing_song) {
+ return streamer_move_nextsong (1);
+ }
+ else {
+ orig_playing_song->played = 0;
+ // find already played song with maximum shuffle rating below prev song
+ int rating = orig_playing_song->shufflerating;
+ playItem_t *pmax = NULL; // played maximum
+ playItem_t *amax = NULL; // absolute maximum
+ for (playItem_t *i = plt->head[PL_MAIN]; i; i = i->next[PL_MAIN]) {
+ if (i != orig_playing_song && i->played && (!amax || i->shufflerating > amax->shufflerating)) {
+ amax = i;
+ }
+ if (i == orig_playing_song || i->shufflerating > rating || !i->played) {
+ continue;
+ }
+ if (!pmax || i->shufflerating > pmax->shufflerating) {
+ pmax = i;
+ }
+ }
+ playItem_t *it = pmax;
+ if (!it) {
+ // that means 1st in playlist, take amax
+ if (pl_loop_mode == PLAYBACK_MODE_LOOP_ALL) {
+ if (!amax) {
+ pl_reshuffle (NULL, &amax);
+ }
+ it = amax;
+ }
+ }
+
+ if (!it) {
+ return -1;
+ }
+ int r = pl_get_idx_of (it);
+ streamer_set_nextsong (r, 1);
+ return 0;
+ }
+ }
+ else if (pl_order == PLAYBACK_ORDER_LINEAR) { // linear
+ playItem_t *it = NULL;
+ if (orig_playing_song) {
+ it = orig_playing_song->prev[PL_MAIN];
+ }
+ if (!it) {
+ if (pl_loop_mode == PLAYBACK_MODE_LOOP_ALL) {
+ it = plt->tail[PL_MAIN];
+ }
+ }
+ if (!it) {
+ return -1;
+ }
+ int r = pl_get_idx_of (it);
+ streamer_set_nextsong (r, 1);
+ return 0;
+ }
+ else if (pl_order == PLAYBACK_ORDER_RANDOM) { // random
+ streamer_move_randomsong ();
+ }
+ return -1;
+}
+
+int
+streamer_move_nextsong (int reason) {
+ playlist_t *plt = plt_get_curr_ptr ();
+ playItem_t *it = pl_playqueue_getnext ();
+ if (it) {
+ pl_playqueue_pop ();
+ int r = pl_get_idx_of (it);
+ streamer_set_nextsong (r, 1);
+ return 0;
+ }
+
+ playItem_t *curr = streamer_get_streaming_track ();
+ if (!plt->head[PL_MAIN]) {
+ streamer_set_nextsong (-2, 1);
+ return 0;
+ }
+ int pl_order = conf_get_int ("playback.order", 0);
+ int pl_loop_mode = conf_get_int ("playback.loop", 0);
+ if (pl_order == PLAYBACK_ORDER_SHUFFLE) { // shuffle
+ if (!curr) {
+ // find minimal notplayed
+ playItem_t *pmin = NULL; // notplayed minimum
+ for (playItem_t *i = plt->head[PL_MAIN]; i; i = i->next[PL_MAIN]) {
+ if (i->played) {
+ continue;
+ }
+ if (!pmin || i->shufflerating < pmin->shufflerating) {
+ pmin = i;
+ }
+ }
+ playItem_t *it = pmin;
+ if (!it) {
+ // all songs played, reshuffle and try again
+ if (pl_loop_mode == PLAYBACK_MODE_LOOP_ALL) { // loop
+ pl_reshuffle (&it, NULL);
+ }
+ }
+ if (!it) {
+ return -1;
+ }
+ int r = pl_get_idx_of (it);
+ streamer_set_nextsong (r, 1);
+ return 0;
+ }
+ else {
+ trace ("pl_next_song: reason=%d, loop=%d\n", reason, pl_loop_mode);
+ if (reason == 0 && pl_loop_mode == PLAYBACK_MODE_LOOP_SINGLE) { // song finished, loop mode is "loop 1 track"
+ int r = pl_get_idx_of (curr);
+ streamer_set_nextsong (r, 1);
+ return 0;
+ }
+ // find minimal notplayed above current
+ int rating = curr->shufflerating;
+ playItem_t *pmin = NULL; // notplayed minimum
+ for (playItem_t *i = plt->head[PL_MAIN]; i; i = i->next[PL_MAIN]) {
+ if (i->played || i->shufflerating < rating) {
+ continue;
+ }
+ if (!pmin || i->shufflerating < pmin->shufflerating) {
+ pmin = i;
+ }
+ }
+ playItem_t *it = pmin;
+ if (!it) {
+ trace ("all songs played! reshuffle\n");
+ // all songs played, reshuffle and try again
+ if (pl_loop_mode == PLAYBACK_MODE_LOOP_ALL) { // loop
+ pl_reshuffle (&it, NULL);
+ }
+ }
+ if (!it) {
+ return -1;
+ }
+ int r = pl_get_idx_of (it);
+ streamer_set_nextsong (r, 1);
+ return 0;
+ }
+ }
+ else if (pl_order == PLAYBACK_ORDER_LINEAR) { // linear
+ playItem_t *it = NULL;
+ if (curr) {
+ if (reason == 0 && pl_loop_mode == PLAYBACK_MODE_LOOP_SINGLE) { // loop same track
+ int r = pl_get_idx_of (curr);
+ streamer_set_nextsong (r, 1);
+ return 0;
+ }
+ it = curr->next[PL_MAIN];
+ }
+ if (!it) {
+ trace ("streamer_move_nextsong: was last track\n");
+ if (pl_loop_mode == PLAYBACK_MODE_LOOP_ALL) {
+ it = plt->head[PL_MAIN];
+ }
+ else {
+ streamer_set_nextsong (-2, 1);
+ return 0;
+ }
+ }
+ if (!it) {
+ return -1;
+ }
+ int r = pl_get_idx_of (it);
+ streamer_set_nextsong (r, 1);
+ return 0;
+ }
+ else if (pl_order == PLAYBACK_ORDER_RANDOM) { // random
+ if (reason == 0 && pl_loop_mode == PLAYBACK_MODE_LOOP_SINGLE && curr) {
+ int r = pl_get_idx_of (curr);
+ streamer_set_nextsong (r, 1);
+ return 0;
+ }
+ return streamer_move_randomsong ();
+ }
+ return -1;
+}
+
+int
+streamer_move_randomsong (void) {
+ int cnt = pl_getcount (PL_MAIN);
+ if (!cnt) {
+ return -1;
+ }
+ int r = rand () / (float)RAND_MAX * cnt;
+ streamer_set_nextsong (r, 1);
+ return 0;
+}
+
+playItem_t *
+streamer_get_current (void) {
+ return orig_playing_song;
+}
+
+struct DB_fileinfo_s *
+streamer_get_current_decoder (void) {
+ return str_current_decoder;
+}