diff options
Diffstat (limited to 'streamer.c')
-rw-r--r-- | streamer.c | 370 |
1 files changed, 293 insertions, 77 deletions
@@ -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; +} |