2010-04-04 06:43:15 -04:00
/*
2011-03-26 13:33:19 -04:00
* Copyright ( C ) 2010 - 2011 Julien BLACHE < jb @ jblache . org >
2016-01-23 19:14:07 -05:00
* Copyright ( C ) 2016 Espen Jürgensen < espenjurgensen @ gmail . com >
2010-04-04 06:43:15 -04:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# ifdef HAVE_CONFIG_H
# include <config.h>
# endif
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <fcntl.h>
# include <string.h>
# include <inttypes.h>
# include <stdint.h>
# include <errno.h>
# include <time.h>
# include <pthread.h>
2016-05-05 13:39:15 -04:00
# ifdef HAVE_PTHREAD_NP_H
# include <pthread_np.h>
# endif
2010-04-04 06:43:15 -04:00
2010-05-01 13:24:06 -04:00
# if defined(__linux__)
# include <sys / timerfd.h>
# elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
2014-09-27 17:08:43 -04:00
# include <signal.h>
2010-04-04 06:43:15 -04:00
# endif
2015-10-19 15:15:29 -04:00
# include <event2/event.h>
# include <event2/buffer.h>
2010-04-04 06:43:15 -04:00
# include <gcrypt.h>
# include "db.h"
# include "logger.h"
# include "conffile.h"
# include "misc.h"
# include "player.h"
2015-03-31 17:05:24 -04:00
# include "worker.h"
2015-02-21 00:04:17 -05:00
# include "listener.h"
2016-05-15 00:25:56 -04:00
# include "commands.h"
2010-04-04 06:43:15 -04:00
2016-01-17 14:59:16 -05:00
/* Audio outputs */
2016-01-23 19:14:07 -05:00
# include "outputs.h"
2014-08-15 17:36:54 -04:00
2016-01-17 14:59:16 -05:00
/* Audio inputs */
2014-03-31 07:10:18 -04:00
# include "transcode.h"
# include "pipe.h"
2014-03-11 18:20:29 -04:00
# ifdef HAVE_SPOTIFY_H
# include "spotify.h"
# endif
2010-04-04 06:43:15 -04:00
2016-01-17 14:59:16 -05:00
/* Metadata input/output */
# include "http.h"
# ifdef LASTFM
# include "lastfm.h"
# endif
2010-04-04 06:43:15 -04:00
# ifndef MIN
# define MIN(a, b) ((a < b) ? a : b)
# endif
2011-02-17 10:21:35 -05:00
# ifndef MAX
# define MAX(a, b) ((a > b) ? a : b)
# endif
2015-09-27 09:24:04 -04:00
// Default volume (must be from 0 - 100)
# define PLAYER_DEFAULT_VOLUME 50
2016-02-11 02:29:29 -05:00
// Used to keep the player from getting ahead of a rate limited source (see below)
2016-02-11 14:02:17 -05:00
# define PLAYER_TICKS_MAX_OVERRUN 2
2015-09-27 09:24:04 -04:00
2015-08-08 12:02:49 -04:00
struct player_source
{
2015-09-02 12:31:43 -04:00
/* Id of the file/item in the files database */
2015-08-08 12:02:49 -04:00
uint32_t id ;
2015-09-02 12:31:43 -04:00
/* Item-Id of the file/item in the queue */
2015-10-03 03:01:26 -04:00
uint32_t item_id ;
2015-09-02 12:31:43 -04:00
/* Length of the file/item in milliseconds */
2015-08-08 12:02:49 -04:00
uint32_t len_ms ;
enum data_kind data_kind ;
enum media_kind media_kind ;
2016-11-08 15:27:38 -05:00
char * path ;
2015-08-08 12:02:49 -04:00
2015-09-02 12:31:43 -04:00
/* Start time of the media item as rtp-time
The stream - start is the rtp - time the media item did or would have
started playing ( after seek or pause ) , therefor the elapsed time of the
media item is always :
elapsed time = current rtptime - stream - start */
2015-08-08 12:02:49 -04:00
uint64_t stream_start ;
2015-09-02 12:31:43 -04:00
/* Output start time of the media item as rtp-time
The output start time is the rtp - time of the first audio packet send
to the audio outputs .
It differs from stream - start especially after a seek , where the first audio
packet has the next rtp - time as output start and stream start becomes the
rtp - time the media item would have been started playing if the seek did
not happen . */
2015-08-08 12:02:49 -04:00
uint64_t output_start ;
2015-09-02 12:31:43 -04:00
/* End time of media item as rtp-time
The end time is set if the reading ( source_read ) of the media item reached
end of file , until then it is 0. */
2015-08-08 12:02:49 -04:00
uint64_t end ;
2015-10-09 17:58:27 -04:00
struct transcode_ctx * xcode ;
2015-09-02 12:31:43 -04:00
int setup_done ;
2015-08-08 12:02:49 -04:00
struct player_source * play_next ;
} ;
2010-11-19 16:51:46 -05:00
struct volume_param {
int volume ;
uint64_t spk_id ;
} ;
2010-12-02 13:51:08 -05:00
struct spk_enum
{
spk_enum_cb cb ;
void * arg ;
} ;
2015-03-14 16:42:53 -04:00
struct icy_artwork
{
2015-04-09 16:22:42 -04:00
uint32_t id ;
2015-03-14 16:42:53 -04:00
char * artwork_url ;
} ;
2015-04-09 15:04:35 -04:00
struct player_metadata
{
int id ;
uint64_t rtptime ;
uint64_t offset ;
int startup ;
2016-01-23 19:14:07 -05:00
struct output_metadata * omd ;
2015-04-09 15:04:35 -04:00
} ;
2016-05-15 00:25:56 -04:00
struct speaker_set_param
{
uint64_t * device_ids ;
int intval ;
} ;
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
union player_arg
{
struct volume_param vol_param ;
void * noarg ;
struct spk_enum * spk_enum ;
struct output_device * device ;
struct player_status * status ;
struct player_source * ps ;
struct player_metadata * pmd ;
uint32_t * id_ptr ;
struct speaker_set_param speaker_set_param ;
enum repeat_mode mode ;
uint32_t id ;
int intval ;
struct icy_artwork icy ;
2010-04-04 06:43:15 -04:00
} ;
struct event_base * evbase_player ;
static int player_exit ;
static pthread_t tid_player ;
2016-05-15 00:25:56 -04:00
static struct commands_base * cmdbase ;
2010-04-04 06:43:15 -04:00
2016-02-28 04:34:18 -05:00
/* Config values */
2016-09-10 17:26:06 -04:00
static int speaker_autoselect ;
2016-02-28 04:34:18 -05:00
static int clear_queue_on_stop_disabled ;
2010-04-04 06:43:15 -04:00
/* Player status */
static enum play_status player_state ;
static enum repeat_mode repeat ;
static char shuffle ;
/* Playback timer */
2010-05-01 13:24:06 -04:00
# if defined(__linux__)
2014-09-27 16:59:19 -04:00
static int pb_timer_fd ;
# else
timer_t pb_timer ;
# endif
static struct event * pb_timer_ev ;
2010-04-04 06:43:15 -04:00
static struct timespec pb_timer_last ;
2011-02-17 10:21:35 -05:00
static struct timespec packet_timer_last ;
2016-02-11 02:29:29 -05:00
// How often the playback timer triggers player_playback_cb
static struct timespec tick_interval ;
// Timer resolution
2011-02-17 10:21:35 -05:00
static struct timespec timer_res ;
2016-02-11 02:29:29 -05:00
// Time between two packets
static struct timespec packet_time = { 0 , AIRTUNES_V2_STREAM_PERIOD } ;
// Will be positive if we need to skip some source reads (see below)
static int ticks_skip ;
2010-04-04 06:43:15 -04:00
/* Sync values */
static struct timespec pb_pos_stamp ;
static uint64_t pb_pos ;
/* Stream position (packets) */
static uint64_t last_rtptime ;
2016-01-23 19:14:07 -05:00
/* Output devices */
static struct output_device * dev_list ;
2010-04-04 06:43:15 -04:00
2015-10-09 17:58:27 -04:00
/* Output status */
2016-01-23 19:14:07 -05:00
static int output_sessions ;
2010-04-04 06:43:15 -04:00
/* Last commanded volume */
2010-11-19 14:35:12 -05:00
static int master_volume ;
2010-04-04 06:43:15 -04:00
/* Audio source */
static struct player_source * cur_playing ;
static struct player_source * cur_streaming ;
2010-07-31 06:30:51 -04:00
static uint32_t cur_plid ;
2015-05-03 04:34:49 -04:00
static uint32_t cur_plversion ;
2016-04-06 02:18:54 -04:00
2010-04-04 06:43:15 -04:00
static struct evbuffer * audio_buf ;
2016-04-06 02:18:54 -04:00
static uint8_t rawbuf [ STOB ( AIRTUNES_V2_PACKET_SAMPLES ) ] ;
2010-04-04 06:43:15 -04:00
2014-04-19 11:18:20 -04:00
/* Play history */
static struct player_history * history ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
2010-04-04 06:43:15 -04:00
static void
status_update ( enum play_status status )
{
player_state = status ;
2015-02-21 00:04:17 -05:00
listener_notify ( LISTENER_PLAYER ) ;
2010-04-04 06:43:15 -04:00
}
2010-11-19 16:51:46 -05:00
/* Volume helpers */
static int
rel_to_vol ( int relvol )
{
2010-12-04 10:15:49 -05:00
float vol ;
2010-11-19 16:51:46 -05:00
if ( relvol = = 100 )
return master_volume ;
2010-12-04 10:15:49 -05:00
vol = ( ( float ) relvol * ( float ) master_volume ) / 100.0 ;
2010-11-19 16:51:46 -05:00
return ( int ) vol ;
}
static int
vol_to_rel ( int volume )
{
2010-12-04 10:15:49 -05:00
float rel ;
2010-11-19 16:51:46 -05:00
if ( volume = = master_volume )
return 100 ;
2010-12-04 10:15:49 -05:00
rel = ( ( float ) volume / ( float ) master_volume ) * 100.0 ;
2010-11-19 16:51:46 -05:00
return ( int ) rel ;
}
/* Master volume helpers */
static void
volume_master_update ( int newvol )
{
2016-01-23 19:14:07 -05:00
struct output_device * device ;
2010-11-19 16:51:46 -05:00
master_volume = newvol ;
2016-01-23 19:14:07 -05:00
for ( device = dev_list ; device ; device = device - > next )
2010-11-19 16:51:46 -05:00
{
2016-01-23 19:14:07 -05:00
if ( device - > selected )
device - > relvol = vol_to_rel ( device - > volume ) ;
2010-11-19 16:51:46 -05:00
}
}
static void
volume_master_find ( void )
{
2016-01-23 19:14:07 -05:00
struct output_device * device ;
2010-11-19 16:51:46 -05:00
int newmaster ;
newmaster = - 1 ;
2016-01-23 19:14:07 -05:00
for ( device = dev_list ; device ; device = device - > next )
2010-11-19 16:51:46 -05:00
{
2016-01-23 19:14:07 -05:00
if ( device - > selected & & ( device - > volume > newmaster ) )
newmaster = device - > volume ;
2010-11-19 16:51:46 -05:00
}
volume_master_update ( newmaster ) ;
}
2010-11-21 04:05:41 -05:00
/* Device select/deselect hooks */
static void
2016-01-23 19:14:07 -05:00
speaker_select_output ( struct output_device * device )
2010-11-21 04:05:41 -05:00
{
2016-01-23 19:14:07 -05:00
device - > selected = 1 ;
2010-11-19 16:51:46 -05:00
2016-01-23 19:14:07 -05:00
if ( device - > volume > master_volume )
2010-11-19 16:51:46 -05:00
{
2015-09-27 09:24:04 -04:00
if ( player_state = = PLAY_STOPPED | | master_volume = = - 1 )
2016-01-23 19:14:07 -05:00
volume_master_update ( device - > volume ) ;
2010-11-19 16:51:46 -05:00
else
2016-01-23 19:14:07 -05:00
device - > volume = master_volume ;
2010-11-19 16:51:46 -05:00
}
2016-01-23 19:14:07 -05:00
device - > relvol = vol_to_rel ( device - > volume ) ;
2010-11-21 04:05:41 -05:00
}
static void
2016-01-23 19:14:07 -05:00
speaker_deselect_output ( struct output_device * device )
2010-11-21 04:05:41 -05:00
{
2016-01-23 19:14:07 -05:00
device - > selected = 0 ;
2010-11-19 16:51:46 -05:00
2016-01-23 19:14:07 -05:00
if ( device - > volume = = master_volume )
2010-11-19 16:51:46 -05:00
volume_master_find ( ) ;
2010-11-21 04:05:41 -05:00
}
2016-06-11 04:37:09 -04:00
int
player_get_current_pos ( uint64_t * pos , struct timespec * ts , int commit )
2010-04-04 06:43:15 -04:00
{
uint64_t delta ;
int ret ;
2011-02-17 10:21:35 -05:00
ret = clock_gettime_with_res ( CLOCK_MONOTONIC , ts , & timer_res ) ;
2010-04-04 06:43:15 -04:00
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Couldn't get clock: %s \n " , strerror ( errno ) ) ;
return - 1 ;
}
delta = ( ts - > tv_sec - pb_pos_stamp . tv_sec ) * 1000000 + ( ts - > tv_nsec - pb_pos_stamp . tv_nsec ) / 1000 ;
# ifdef DEBUG_SYNC
DPRINTF ( E_DBG , L_PLAYER , " Delta is % " PRIu64 " usec \n " , delta ) ;
# endif
delta = ( delta * 44100 ) / 1000000 ;
# ifdef DEBUG_SYNC
DPRINTF ( E_DBG , L_PLAYER , " Delta is % " PRIu64 " samples \n " , delta ) ;
# endif
* pos = pb_pos + delta ;
if ( commit )
{
pb_pos = * pos ;
pb_pos_stamp . tv_sec = ts - > tv_sec ;
pb_pos_stamp . tv_nsec = ts - > tv_nsec ;
2010-11-21 05:59:45 -05:00
# ifdef DEBUG_SYNC
2010-04-04 06:43:15 -04:00
DPRINTF ( E_DBG , L_PLAYER , " Pos: % " PRIu64 " (clock) \n " , * pos ) ;
2010-11-21 05:59:45 -05:00
# endif
2010-04-04 06:43:15 -04:00
}
return 0 ;
}
2014-09-27 16:59:19 -04:00
static int
2016-02-11 02:29:29 -05:00
pb_timer_start ( void )
2014-09-27 16:59:19 -04:00
{
2016-02-11 02:29:29 -05:00
struct itimerspec tick ;
2014-09-27 16:59:19 -04:00
int ret ;
2016-02-11 02:29:29 -05:00
tick . it_interval = tick_interval ;
tick . it_value = tick_interval ;
2014-09-27 16:59:19 -04:00
# if defined(__linux__)
2016-02-11 02:29:29 -05:00
ret = timerfd_settime ( pb_timer_fd , 0 , & tick , NULL ) ;
2014-09-27 16:59:19 -04:00
# else
2016-02-11 02:29:29 -05:00
ret = timer_settime ( pb_timer , 0 , & tick , NULL ) ;
# endif
2014-09-27 16:59:19 -04:00
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Could not arm playback timer: %s \n " , strerror ( errno ) ) ;
return - 1 ;
}
return 0 ;
}
static int
pb_timer_stop ( void )
{
2016-02-11 02:29:29 -05:00
struct itimerspec tick ;
2014-09-27 16:59:19 -04:00
int ret ;
2016-02-11 02:29:29 -05:00
memset ( & tick , 0 , sizeof ( struct itimerspec ) ) ;
2014-09-27 16:59:19 -04:00
# if defined(__linux__)
2016-02-11 02:29:29 -05:00
ret = timerfd_settime ( pb_timer_fd , 0 , & tick , NULL ) ;
2014-09-27 16:59:19 -04:00
# else
2016-02-11 02:29:29 -05:00
ret = timer_settime ( pb_timer , 0 , & tick , NULL ) ;
2014-09-27 16:59:19 -04:00
# endif
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Could not disarm playback timer: %s \n " , strerror ( errno ) ) ;
return - 1 ;
}
return 0 ;
}
2010-04-04 06:43:15 -04:00
/* Forward */
2010-12-12 04:12:39 -05:00
static void
playback_abort ( void ) ;
2010-04-04 06:43:15 -04:00
2015-04-07 17:35:56 -04:00
static void
2015-04-09 15:04:35 -04:00
player_metadata_send ( struct player_metadata * pmd ) ;
2015-04-07 17:35:56 -04:00
/* Callback from the worker thread (async operation as it may block) */
2015-03-31 17:05:24 -04:00
static void
playcount_inc_cb ( void * arg )
{
int * id = arg ;
db_file_inc_playcount ( * id ) ;
}
2015-06-27 17:24:07 -04:00
# ifdef LASTFM
/* Callback from the worker thread (async operation as it may block) */
static void
scrobble_cb ( void * arg )
{
int * id = arg ;
lastfm_scrobble ( * id ) ;
}
# endif
2015-04-07 17:35:56 -04:00
/* Callback from the worker thread
* This prepares metadata in the worker thread , since especially the artwork
2016-01-23 19:14:07 -05:00
* retrieval may take some time . outputs_metadata_prepare ( ) must be thread safe .
* The sending must be done in the player thread .
2015-04-07 17:35:56 -04:00
*/
2015-03-31 17:05:24 -04:00
static void
2015-04-07 17:35:56 -04:00
metadata_prepare_cb ( void * arg )
2015-03-31 17:05:24 -04:00
{
2015-04-09 15:04:35 -04:00
struct player_metadata * pmd = arg ;
2015-03-31 17:05:24 -04:00
2016-01-23 19:14:07 -05:00
pmd - > omd = outputs_metadata_prepare ( pmd - > id ) ;
2015-04-07 17:35:56 -04:00
2016-01-23 19:14:07 -05:00
if ( pmd - > omd )
2015-04-09 15:04:35 -04:00
player_metadata_send ( pmd ) ;
2016-01-23 19:14:07 -05:00
outputs_metadata_free ( pmd - > omd ) ;
2015-03-31 17:05:24 -04:00
}
2015-04-07 17:35:56 -04:00
/* Callback from the worker thread (async operation as it may block) */
2015-03-31 17:05:24 -04:00
static void
update_icy_cb ( void * arg )
{
struct http_icy_metadata * metadata = arg ;
2016-11-12 07:23:41 -05:00
db_queue_update_icymetadata ( metadata - > id , metadata - > artist , metadata - > title ) ;
2015-03-31 17:05:24 -04:00
http_icy_metadata_free ( metadata , 1 ) ;
}
2010-04-04 06:43:15 -04:00
2011-03-26 13:33:19 -04:00
/* Metadata */
static void
metadata_prune ( uint64_t pos )
{
2016-01-23 19:14:07 -05:00
outputs_metadata_prune ( pos ) ;
2011-03-26 13:33:19 -04:00
}
static void
metadata_purge ( void )
{
2016-01-23 19:14:07 -05:00
outputs_metadata_purge ( ) ;
2011-03-26 13:33:19 -04:00
}
static void
2015-09-26 02:45:52 -04:00
metadata_trigger ( int startup )
2011-03-26 13:33:19 -04:00
{
2015-04-09 15:04:35 -04:00
struct player_metadata pmd ;
2011-03-26 13:33:19 -04:00
2015-04-09 15:04:35 -04:00
memset ( & pmd , 0 , sizeof ( struct player_metadata ) ) ;
2015-09-26 02:45:52 -04:00
pmd . id = cur_streaming - > id ;
2015-04-09 15:04:35 -04:00
pmd . startup = startup ;
2011-03-26 13:33:19 -04:00
2015-09-26 02:45:52 -04:00
if ( cur_streaming - > stream_start & & cur_streaming - > output_start )
2011-03-26 13:33:19 -04:00
{
2015-09-26 02:45:52 -04:00
pmd . offset = cur_streaming - > output_start - cur_streaming - > stream_start ;
pmd . rtptime = cur_streaming - > stream_start ;
2011-03-26 13:33:19 -04:00
}
else
{
2015-04-07 17:35:56 -04:00
DPRINTF ( E_LOG , L_PLAYER , " PTOH! Unhandled song boundary case in metadata_trigger() \n " ) ;
2011-03-26 13:33:19 -04:00
}
2015-04-07 17:35:56 -04:00
/* Defer the actual work of preparing the metadata to the worker thread */
2015-04-09 15:04:35 -04:00
worker_execute ( metadata_prepare_cb , & pmd , sizeof ( struct player_metadata ) , 0 ) ;
2011-03-26 13:33:19 -04:00
}
2015-04-11 14:30:31 -04:00
/* Checks if there is new HTTP ICY metadata, and if so sends updates to clients */
void
metadata_check_icy ( void )
2015-03-14 16:42:53 -04:00
{
2015-03-20 18:40:42 -04:00
struct http_icy_metadata * metadata ;
2015-03-14 16:42:53 -04:00
int changed ;
2015-10-09 17:58:27 -04:00
metadata = transcode_metadata ( cur_streaming - > xcode , & changed ) ;
2015-03-14 16:42:53 -04:00
if ( ! metadata )
2015-04-11 14:30:31 -04:00
return ;
2015-03-14 16:42:53 -04:00
2015-04-12 12:15:06 -04:00
if ( ! changed | | ! metadata - > title )
goto no_update ;
if ( metadata - > title [ 0 ] = = ' \0 ' )
2015-03-14 16:42:53 -04:00
goto no_update ;
2016-11-12 07:23:41 -05:00
metadata - > id = cur_streaming - > item_id ;
2015-03-31 17:05:24 -04:00
/* Defer the database update to the worker thread */
worker_execute ( update_icy_cb , metadata , sizeof ( struct http_icy_metadata ) , 0 ) ;
2016-01-23 19:14:07 -05:00
/* Triggers preparing and sending output metadata */
2015-09-26 02:45:52 -04:00
metadata_trigger ( 0 ) ;
2015-03-14 16:42:53 -04:00
2015-03-31 17:05:24 -04:00
/* Only free the struct, the content must be preserved for update_icy_cb */
free ( metadata ) ;
2015-04-11 14:30:31 -04:00
status_update ( player_state ) ;
2015-03-31 17:05:24 -04:00
return ;
2015-03-14 16:42:53 -04:00
no_update :
2015-03-31 17:05:24 -04:00
http_icy_metadata_free ( metadata , 0 ) ;
2015-03-14 16:42:53 -04:00
}
2015-09-02 12:31:43 -04:00
struct player_history *
player_history_get ( void )
{
return history ;
}
2015-06-07 18:21:49 -04:00
2015-09-02 12:31:43 -04:00
/*
* Add the song with the given id to the list of previously played songs
*/
static void
2015-10-03 03:01:26 -04:00
history_add ( uint32_t id , uint32_t item_id )
2010-04-04 06:43:15 -04:00
{
2015-09-02 12:31:43 -04:00
unsigned int cur_index ;
unsigned int next_index ;
2010-07-31 05:41:36 -04:00
2015-09-02 12:31:43 -04:00
/* Check if the current song is already the last in the history to avoid duplicates */
cur_index = ( history - > start_index + history - > count - 1 ) % MAX_HISTORY_COUNT ;
if ( id = = history - > id [ cur_index ] )
{
2015-09-26 02:45:52 -04:00
DPRINTF ( E_DBG , L_PLAYER , " Current playing/streaming song already in history \n " ) ;
2015-09-02 12:31:43 -04:00
return ;
}
2010-07-31 05:41:36 -04:00
2015-09-02 12:31:43 -04:00
/* Calculate the next index and update the start-index and count for the id-buffer */
next_index = ( history - > start_index + history - > count ) % MAX_HISTORY_COUNT ;
if ( next_index = = history - > start_index & & history - > count > 0 )
history - > start_index = ( history - > start_index + 1 ) % MAX_HISTORY_COUNT ;
2010-07-31 05:41:36 -04:00
2015-09-02 12:31:43 -04:00
history - > id [ next_index ] = id ;
2015-10-03 03:01:26 -04:00
history - > item_id [ next_index ] = item_id ;
2015-09-02 12:31:43 -04:00
if ( history - > count < MAX_HISTORY_COUNT )
history - > count + + ;
2010-07-31 05:41:36 -04:00
}
2015-09-02 12:31:43 -04:00
/* Audio sources */
/*
* Initializes the given player source for playback
*/
static int
2016-11-08 15:27:38 -05:00
stream_setup ( struct player_source * ps )
2010-04-04 06:43:15 -04:00
{
2015-09-02 12:31:43 -04:00
char * url ;
int ret ;
2016-11-08 15:27:38 -05:00
if ( ! ps )
2015-09-02 12:31:43 -04:00
{
2016-11-08 15:27:38 -05:00
DPRINTF ( E_LOG , L_PLAYER , " No player source given to stream_setup \n " ) ;
2015-09-02 12:31:43 -04:00
return - 1 ;
}
if ( ps - > setup_done )
{
2015-09-13 01:58:27 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Given player source already setup (id = %d) \n " , ps - > id ) ;
2015-09-02 12:31:43 -04:00
return - 1 ;
}
// Setup depending on data kind
2015-08-04 16:33:32 -04:00
switch ( ps - > data_kind )
2014-03-11 18:20:29 -04:00
{
2015-08-04 16:33:32 -04:00
case DATA_KIND_FILE :
2016-11-08 15:27:38 -05:00
ps - > xcode = transcode_setup ( ps - > data_kind , ps - > path , ps - > len_ms , XCODE_PCM16_NOHEADER , NULL ) ;
2015-10-09 17:58:27 -04:00
ret = ps - > xcode ? 0 : - 1 ;
2015-09-02 12:31:43 -04:00
break ;
2015-08-04 16:33:32 -04:00
case DATA_KIND_HTTP :
2016-11-08 15:27:38 -05:00
ret = http_stream_setup ( & url , ps - > path ) ;
2015-09-02 12:31:43 -04:00
if ( ret < 0 )
break ;
2016-11-08 15:27:38 -05:00
free ( ps - > path ) ;
ps - > path = url ;
2015-09-02 12:31:43 -04:00
2016-11-08 15:27:38 -05:00
ps - > xcode = transcode_setup ( ps - > data_kind , ps - > path , ps - > len_ms , XCODE_PCM16_NOHEADER , NULL ) ;
2015-10-09 17:58:27 -04:00
ret = ps - > xcode ? 0 : - 1 ;
2014-03-11 18:20:29 -04:00
break ;
2015-08-04 16:33:32 -04:00
case DATA_KIND_SPOTIFY :
2014-03-11 18:20:29 -04:00
# ifdef HAVE_SPOTIFY_H
2016-11-08 15:27:38 -05:00
ret = spotify_playback_setup ( ps - > path ) ;
2015-09-02 12:31:43 -04:00
# else
2016-11-08 15:27:38 -05:00
DPRINTF ( E_LOG , L_PLAYER , " Player source has data kind 'spotify' (%d), but forked-daapd is compiled without spotify support - cannot setup source '%s' \n " ,
ps - > data_kind , ps - > path ) ;
2015-09-02 12:31:43 -04:00
ret = - 1 ;
2014-03-11 18:20:29 -04:00
# endif
break ;
2014-03-31 07:10:18 -04:00
2015-08-04 16:33:32 -04:00
case DATA_KIND_PIPE :
2016-11-08 15:27:38 -05:00
ret = pipe_setup ( ps - > path ) ;
2014-03-31 07:10:18 -04:00
break ;
2015-09-02 12:31:43 -04:00
default :
2016-11-08 15:27:38 -05:00
DPRINTF ( E_LOG , L_PLAYER , " Unknown data kind (%d) for player source - cannot setup source '%s' \n " ,
ps - > data_kind , ps - > path ) ;
2015-09-02 12:31:43 -04:00
ret = - 1 ;
2014-03-11 18:20:29 -04:00
}
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
if ( ret = = 0 )
2015-09-13 01:58:27 -04:00
ps - > setup_done = 1 ;
else
DPRINTF ( E_LOG , L_PLAYER , " Failed to setup player source (id = %d) \n " , ps - > id ) ;
2015-09-02 12:31:43 -04:00
return ret ;
2010-04-04 06:43:15 -04:00
}
2015-09-02 12:31:43 -04:00
/*
* Starts or resumes plaback for the given player source
*/
static int
stream_play ( struct player_source * ps )
2010-04-04 06:43:15 -04:00
{
2015-09-02 12:31:43 -04:00
int ret ;
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
if ( ! ps )
2010-04-04 06:43:15 -04:00
{
2015-09-12 01:45:31 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Stream play called with no active streaming player source \n " ) ;
2015-09-02 12:31:43 -04:00
return - 1 ;
}
if ( ! ps - > setup_done )
{
2015-09-12 01:45:31 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Given player source not setup, play not possible \n " ) ;
2015-09-02 12:31:43 -04:00
return - 1 ;
}
// Start/resume playback depending on data kind
switch ( ps - > data_kind )
{
case DATA_KIND_HTTP :
case DATA_KIND_FILE :
ret = 0 ;
break ;
2014-03-11 18:20:29 -04:00
# ifdef HAVE_SPOTIFY_H
2015-09-02 12:31:43 -04:00
case DATA_KIND_SPOTIFY :
2015-09-12 01:45:31 -04:00
ret = spotify_playback_play ( ) ;
2015-09-02 12:31:43 -04:00
break ;
2014-03-11 18:20:29 -04:00
# endif
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
case DATA_KIND_PIPE :
ret = 0 ;
break ;
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
default :
ret = - 1 ;
2010-04-04 06:43:15 -04:00
}
2015-09-02 12:31:43 -04:00
return ret ;
2010-04-04 06:43:15 -04:00
}
2015-09-02 12:31:43 -04:00
/*
* Read up to " len " data from the given player source and returns
* the actual amount of data read .
*/
2010-04-04 06:43:15 -04:00
static int
2015-09-02 12:31:43 -04:00
stream_read ( struct player_source * ps , int len )
2010-04-04 06:43:15 -04:00
{
2015-09-02 12:31:43 -04:00
int icy_timer ;
2014-03-11 18:20:29 -04:00
int ret ;
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
if ( ! ps )
2010-04-04 06:43:15 -04:00
{
2015-09-02 12:31:43 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Stream read called with no active streaming player source \n " ) ;
return - 1 ;
}
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
if ( ! ps - > setup_done )
{
DPRINTF ( E_LOG , L_PLAYER , " Given player source not setup for reading data \n " ) ;
2010-04-04 06:43:15 -04:00
return - 1 ;
}
2015-09-02 12:31:43 -04:00
// Read up to len data depending on data kind
switch ( ps - > data_kind )
2010-04-04 06:43:15 -04:00
{
2015-09-02 12:31:43 -04:00
case DATA_KIND_HTTP :
2015-10-22 16:09:19 -04:00
ret = transcode ( audio_buf , len , ps - > xcode , & icy_timer ) ;
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
if ( icy_timer )
metadata_check_icy ( ) ;
break ;
case DATA_KIND_FILE :
2015-10-22 16:09:19 -04:00
ret = transcode ( audio_buf , len , ps - > xcode , & icy_timer ) ;
2015-09-02 12:31:43 -04:00
break ;
# ifdef HAVE_SPOTIFY_H
case DATA_KIND_SPOTIFY :
ret = spotify_audio_get ( audio_buf , len ) ;
break ;
# endif
case DATA_KIND_PIPE :
ret = pipe_audio_get ( audio_buf , len ) ;
break ;
default :
ret = - 1 ;
2010-04-04 06:43:15 -04:00
}
2015-09-02 12:31:43 -04:00
return ret ;
}
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
/*
* Pauses playback of the given player source
*/
static int
stream_pause ( struct player_source * ps )
{
int ret ;
2015-08-04 16:33:32 -04:00
2015-09-02 12:31:43 -04:00
if ( ! ps )
2014-03-11 18:20:29 -04:00
{
2015-09-02 12:31:43 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Stream pause called with no active streaming player source \n " ) ;
return - 1 ;
}
2015-03-20 18:40:42 -04:00
2015-09-02 12:31:43 -04:00
if ( ! ps - > setup_done )
{
DPRINTF ( E_LOG , L_PLAYER , " Given player source not setup, pause not possible \n " ) ;
return - 1 ;
}
2015-03-20 18:40:42 -04:00
2015-09-02 12:31:43 -04:00
// Pause playback depending on data kind
switch ( ps - > data_kind )
{
case DATA_KIND_HTTP :
2015-10-25 03:58:47 -04:00
ret = 0 ;
break ;
2015-09-02 12:31:43 -04:00
case DATA_KIND_FILE :
ret = 0 ;
2015-03-14 16:42:53 -04:00
break ;
2014-03-11 18:20:29 -04:00
# ifdef HAVE_SPOTIFY_H
2015-09-02 12:31:43 -04:00
case DATA_KIND_SPOTIFY :
2015-09-13 01:58:27 -04:00
ret = spotify_playback_pause ( ) ;
2014-03-31 07:10:18 -04:00
break ;
2015-09-02 12:31:43 -04:00
# endif
2014-03-31 07:10:18 -04:00
2015-04-23 05:34:44 -04:00
case DATA_KIND_PIPE :
2015-09-02 12:31:43 -04:00
ret = 0 ;
2014-03-31 07:10:18 -04:00
break ;
default :
2015-09-02 12:31:43 -04:00
ret = - 1 ;
2014-03-11 18:20:29 -04:00
}
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
return ret ;
}
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
/*
2015-09-20 01:11:39 -04:00
* Seeks to the given position in milliseconds of the given player source
2015-09-02 12:31:43 -04:00
*/
static int
stream_seek ( struct player_source * ps , int seek_ms )
{
int ret ;
if ( ! ps )
2010-04-04 06:43:15 -04:00
{
2015-09-02 12:31:43 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Stream seek called with no active streaming player source \n " ) ;
return - 1 ;
}
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
if ( ! ps - > setup_done )
{
DPRINTF ( E_LOG , L_PLAYER , " Given player source not setup, seek not possible \n " ) ;
2010-04-04 06:43:15 -04:00
return - 1 ;
}
2015-09-02 12:31:43 -04:00
// Seek depending on data kind
switch ( ps - > data_kind )
{
case DATA_KIND_HTTP :
ret = 0 ;
break ;
2011-03-26 13:33:19 -04:00
2015-09-02 12:31:43 -04:00
case DATA_KIND_FILE :
2015-10-09 17:58:27 -04:00
ret = transcode_seek ( ps - > xcode , seek_ms ) ;
2015-09-02 12:31:43 -04:00
break ;
# ifdef HAVE_SPOTIFY_H
case DATA_KIND_SPOTIFY :
ret = spotify_playback_seek ( seek_ms ) ;
break ;
# endif
case DATA_KIND_PIPE :
ret = 0 ;
break ;
default :
ret = - 1 ;
}
2014-03-11 18:20:29 -04:00
2015-08-04 16:33:32 -04:00
return ret ;
2010-04-04 06:43:15 -04:00
}
2015-09-02 12:31:43 -04:00
/*
2015-09-18 13:19:02 -04:00
* Stops playback and cleanup for the given player source
2015-09-02 12:31:43 -04:00
*/
2010-04-04 06:43:15 -04:00
static int
2015-09-02 12:31:43 -04:00
stream_stop ( struct player_source * ps )
{
if ( ! ps )
2010-04-04 06:43:15 -04:00
{
2015-09-02 12:31:43 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Stream cleanup called with no active streaming player source \n " ) ;
return - 1 ;
}
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
if ( ! ps - > setup_done )
{
DPRINTF ( E_LOG , L_PLAYER , " Given player source not setup, cleanup not possible \n " ) ;
return - 1 ;
2015-08-08 12:02:49 -04:00
}
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
switch ( ps - > data_kind )
2015-08-08 12:02:49 -04:00
{
2015-09-02 12:31:43 -04:00
case DATA_KIND_FILE :
case DATA_KIND_HTTP :
2015-10-09 17:58:27 -04:00
if ( ps - > xcode )
2015-09-02 12:31:43 -04:00
{
2015-10-09 17:58:27 -04:00
transcode_cleanup ( ps - > xcode ) ;
ps - > xcode = NULL ;
2015-09-02 12:31:43 -04:00
}
break ;
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
case DATA_KIND_SPOTIFY :
# ifdef HAVE_SPOTIFY_H
2015-09-18 14:01:20 -04:00
spotify_playback_stop ( ) ;
2015-09-02 12:31:43 -04:00
# endif
break ;
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
case DATA_KIND_PIPE :
pipe_cleanup ( ) ;
break ;
}
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
ps - > setup_done = 0 ;
return 0 ;
}
static struct player_source *
source_now_playing ( )
{
if ( cur_playing )
return cur_playing ;
return cur_streaming ;
}
/*
* Creates a new player source for the given queue item
*/
static struct player_source *
2016-11-08 15:27:38 -05:00
source_new ( struct db_queue_item * queue_item )
2015-09-02 12:31:43 -04:00
{
struct player_source * ps ;
2016-11-19 17:08:50 -05:00
ps = calloc ( 1 , sizeof ( struct player_source ) ) ;
if ( ! ps )
{
DPRINTF ( E_LOG , L_PLAYER , " Out of memory (ps) \n " ) ;
return NULL ;
}
2015-09-02 12:31:43 -04:00
2016-11-08 15:27:38 -05:00
ps - > id = queue_item - > file_id ;
ps - > item_id = queue_item - > item_id ;
ps - > data_kind = queue_item - > data_kind ;
ps - > media_kind = queue_item - > media_kind ;
ps - > len_ms = queue_item - > song_length ;
2015-09-02 12:31:43 -04:00
ps - > play_next = NULL ;
2016-11-08 15:27:38 -05:00
ps - > path = strdup ( queue_item - > path ) ;
2015-09-02 12:31:43 -04:00
return ps ;
}
2016-11-08 15:27:38 -05:00
static void
source_free ( struct player_source * ps )
{
if ( ps - > path )
free ( ps - > path ) ;
free ( ps ) ;
}
2015-09-02 12:31:43 -04:00
/*
* Stops playback for the current streaming source and frees all
* player sources ( starting from the playing source ) . Sets current streaming
* and playing sources to NULL .
*/
2015-09-20 01:11:39 -04:00
static void
2015-09-02 12:31:43 -04:00
source_stop ( )
{
struct player_source * ps_playing ;
struct player_source * ps_temp ;
2015-09-20 01:11:39 -04:00
if ( cur_streaming )
stream_stop ( cur_streaming ) ;
2015-09-02 12:31:43 -04:00
ps_playing = source_now_playing ( ) ;
while ( ps_playing )
{
ps_temp = ps_playing ;
ps_playing = ps_playing - > play_next ;
ps_temp - > play_next = NULL ;
2016-11-08 15:27:38 -05:00
source_free ( ps_temp ) ;
2010-04-04 06:43:15 -04:00
}
2015-09-02 12:31:43 -04:00
cur_playing = NULL ;
cur_streaming = NULL ;
}
/*
* Pauses playback
*
* Resets the streaming source to the playing source and adjusts stream - start
* and output - start values to the playing time . Sets the current streaming
* source to NULL .
*/
static int
source_pause ( uint64_t pos )
{
struct player_source * ps_playing ;
struct player_source * ps_playnext ;
struct player_source * ps_temp ;
uint64_t seek_frames ;
int seek_ms ;
int ret ;
ps_playing = source_now_playing ( ) ;
2016-11-19 17:08:50 -05:00
if ( ! ps_playing )
return - 1 ;
2015-09-02 12:31:43 -04:00
2015-09-18 14:01:20 -04:00
if ( cur_streaming )
2015-09-13 01:58:27 -04:00
{
2015-09-18 14:01:20 -04:00
if ( ps_playing ! = cur_streaming )
{
DPRINTF ( E_DBG , L_PLAYER ,
" Pause called on playing source (id=%d) and streaming source already "
" switched to the next item (id=%d) \n " , ps_playing - > id , cur_streaming - > id ) ;
ret = stream_stop ( cur_streaming ) ;
if ( ret < 0 )
return - 1 ;
}
else
{
ret = stream_pause ( cur_streaming ) ;
if ( ret < 0 )
return - 1 ;
}
2015-09-13 01:58:27 -04:00
}
2015-09-02 12:31:43 -04:00
ps_playnext = ps_playing - > play_next ;
while ( ps_playnext )
{
ps_temp = ps_playnext ;
ps_playnext = ps_playnext - > play_next ;
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
ps_temp - > play_next = NULL ;
2016-11-08 15:27:38 -05:00
source_free ( ps_temp ) ;
2010-04-04 06:43:15 -04:00
}
2015-09-02 12:31:43 -04:00
ps_playing - > play_next = NULL ;
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
cur_playing = NULL ;
cur_streaming = ps_playing ;
if ( ! cur_streaming - > setup_done )
2010-04-04 06:43:15 -04:00
{
2016-10-26 14:09:09 -04:00
DPRINTF ( E_INFO , L_PLAYER , " Opening '%s' \n " , cur_streaming - > path ) ;
2015-09-02 12:31:43 -04:00
2016-11-08 15:27:38 -05:00
ret = stream_setup ( cur_streaming ) ;
2015-09-13 01:58:27 -04:00
if ( ret < 0 )
{
2016-10-26 14:09:09 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Failed to open '%s' \n " , cur_streaming - > path ) ;
2015-09-13 01:58:27 -04:00
return - 1 ;
}
2010-04-04 06:43:15 -04:00
}
2015-09-13 01:58:27 -04:00
/* Seek back to the pause position */
2015-09-02 12:31:43 -04:00
seek_frames = ( pos - cur_streaming - > stream_start ) ;
seek_ms = ( int ) ( ( seek_frames * 1000 ) / 44100 ) ;
ret = stream_seek ( cur_streaming , seek_ms ) ;
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
/* Adjust start_pos to take into account the pause and seek back */
cur_streaming - > stream_start = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES - ( ( uint64_t ) ret * 44100 ) / 1000 ;
cur_streaming - > output_start = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES ;
cur_streaming - > end = 0 ;
2010-04-04 06:43:15 -04:00
return 0 ;
}
2015-09-02 12:31:43 -04:00
/*
* Seeks the current streaming source to the given postion in milliseconds
* and adjusts stream - start and output - start values .
2015-09-20 01:11:39 -04:00
*
* @ param seek_ms Position in milliseconds to seek
* @ return The new position in milliseconds or - 1 on error
2015-09-02 12:31:43 -04:00
*/
2010-04-04 06:43:15 -04:00
static int
2015-09-02 12:31:43 -04:00
source_seek ( int seek_ms )
2010-04-04 06:43:15 -04:00
{
int ret ;
2015-09-02 12:31:43 -04:00
ret = stream_seek ( cur_streaming , seek_ms ) ;
2015-09-13 01:58:27 -04:00
if ( ret < 0 )
return - 1 ;
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
/* Adjust start_pos to take into account the pause and seek back */
cur_streaming - > stream_start = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES - ( ( uint64_t ) ret * 44100 ) / 1000 ;
cur_streaming - > output_start = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES ;
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
return ret ;
}
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
/*
* Starts or resumes playback
*/
static int
source_play ( )
{
int ret ;
ret = stream_play ( cur_streaming ) ;
2016-04-06 02:18:54 -04:00
ticks_skip = 0 ;
memset ( rawbuf , 0 , sizeof ( rawbuf ) ) ;
2015-09-02 12:31:43 -04:00
return ret ;
}
/*
2016-11-08 15:27:38 -05:00
* Opens the given player source for playback ( but does not start playback )
2015-09-02 12:31:43 -04:00
*
2016-11-08 15:27:38 -05:00
* The given source is appended to the current streaming source ( if one exists ) and
* becomes the new current streaming source .
2015-09-02 12:31:43 -04:00
*
* Stream - start and output - start values are set to the given start position .
*/
static int
2016-11-08 15:27:38 -05:00
source_open ( struct player_source * ps , uint64_t start_pos , int seek_ms )
2015-09-02 12:31:43 -04:00
{
int ret ;
2016-11-08 15:27:38 -05:00
DPRINTF ( E_INFO , L_PLAYER , " Opening '%s' (id=%d, item-id=%d) \n " , ps - > path , ps - > id , ps - > item_id ) ;
2010-04-04 06:43:15 -04:00
2016-11-08 15:27:38 -05:00
if ( cur_streaming & & cur_streaming - > end = = 0 )
2010-04-04 06:43:15 -04:00
{
2016-11-08 15:27:38 -05:00
DPRINTF ( E_LOG , L_PLAYER , " Current streaming source not at eof '%s' (id=%d, item-id=%d) \n " ,
cur_streaming - > path , cur_streaming - > id , cur_streaming - > item_id ) ;
2010-04-04 06:43:15 -04:00
return - 1 ;
}
2016-11-08 15:27:38 -05:00
ret = stream_setup ( ps ) ;
2015-09-13 01:58:27 -04:00
if ( ret < 0 )
{
2016-11-08 15:27:38 -05:00
DPRINTF ( E_LOG , L_PLAYER , " Failed to open '%s' (id=%d, item-id=%d) \n " , ps - > path , ps - > id , ps - > item_id ) ;
2015-09-13 01:58:27 -04:00
return - 1 ;
}
2015-09-02 12:31:43 -04:00
2015-09-13 01:58:27 -04:00
/* If a streaming source exists, append the new source as play-next and set it
as the new streaming source */
2015-09-02 12:31:43 -04:00
if ( cur_streaming )
cur_streaming - > play_next = ps ;
cur_streaming = ps ;
cur_streaming - > stream_start = start_pos ;
cur_streaming - > output_start = cur_streaming - > stream_start ;
cur_streaming - > end = 0 ;
2016-11-08 15:27:38 -05:00
// Seek to the given seek position
if ( seek_ms )
{
DPRINTF ( E_INFO , L_PLAYER , " Seek to %d ms for '%s' (id=%d, item-id=%d) \n " , seek_ms , ps - > path , ps - > id , ps - > item_id ) ;
source_seek ( seek_ms ) ;
}
2015-09-02 12:31:43 -04:00
return ret ;
}
/*
* Closes the current streaming source and sets its end - time to the given
* position
*/
static int
source_close ( uint64_t end_pos )
{
2015-09-18 13:19:02 -04:00
stream_stop ( cur_streaming ) ;
2015-09-02 12:31:43 -04:00
cur_streaming - > end = end_pos ;
2010-04-04 06:43:15 -04:00
return 0 ;
}
2015-04-25 01:43:55 -04:00
/*
2015-09-20 01:11:39 -04:00
* Updates the now playing item ( cur_playing ) and notifies remotes and raop devices
* about changes . Also takes care of stopping playback after the last item .
*
* @ return Returns the current playback position as rtp - time
2015-04-25 01:43:55 -04:00
*/
2010-04-04 06:43:15 -04:00
static uint64_t
source_check ( void )
{
struct timespec ts ;
struct player_source * ps ;
uint64_t pos ;
int i ;
2015-03-31 17:05:24 -04:00
int id ;
2010-04-04 06:43:15 -04:00
int ret ;
ret = player_get_current_pos ( & pos , & ts , 0 ) ;
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Couldn't get current playback position \n " ) ;
return 0 ;
}
2016-04-14 02:55:59 -04:00
if ( player_state = = PLAY_STOPPED )
{
DPRINTF ( E_LOG , L_PLAYER , " Bug! source_check called but playback has already stopped \n " ) ;
return pos ;
}
2015-04-25 01:43:55 -04:00
/* If cur_playing is NULL, we are still in the first two seconds after starting the stream */
2010-04-04 06:43:15 -04:00
if ( ! cur_playing )
{
if ( pos > = cur_streaming - > output_start )
{
cur_playing = cur_streaming ;
status_update ( PLAY_PLAYING ) ;
2011-03-26 13:33:19 -04:00
/* Start of streaming, no metadata to prune yet */
2010-04-04 06:43:15 -04:00
}
return pos ;
}
2015-04-25 01:43:55 -04:00
/* Check if we are still in the middle of the current playing song */
2010-04-04 06:43:15 -04:00
if ( ( cur_playing - > end = = 0 ) | | ( pos < cur_playing - > end ) )
return pos ;
2015-04-25 01:43:55 -04:00
/* We have reached the end of the current playing song, update cur_playing to the next song in the queue
and initialize stream_start and output_start values . */
2010-04-04 06:43:15 -04:00
i = 0 ;
while ( cur_playing & & ( cur_playing - > end ! = 0 ) & & ( pos > cur_playing - > end ) )
{
i + + ;
2015-03-31 17:05:24 -04:00
id = ( int ) cur_playing - > id ;
worker_execute ( playcount_inc_cb , & id , sizeof ( int ) , 5 ) ;
2014-08-15 17:36:54 -04:00
# ifdef LASTFM
2015-06-27 17:24:07 -04:00
worker_execute ( scrobble_cb , & id , sizeof ( int ) , 8 ) ;
2014-08-15 17:36:54 -04:00
# endif
2015-10-03 03:01:26 -04:00
history_add ( cur_playing - > id , cur_playing - > item_id ) ;
2013-10-26 17:13:02 -04:00
2015-09-02 12:31:43 -04:00
/* Stop playback */
2015-08-08 12:02:49 -04:00
if ( ! cur_playing - > play_next )
2010-04-04 06:43:15 -04:00
{
2010-12-12 04:12:39 -05:00
playback_abort ( ) ;
2010-04-04 06:43:15 -04:00
return pos ;
}
ps = cur_playing ;
cur_playing = cur_playing - > play_next ;
2016-11-08 15:27:38 -05:00
source_free ( ps ) ;
2010-04-04 06:43:15 -04:00
}
if ( i > 0 )
{
DPRINTF ( E_DBG , L_PLAYER , " Playback switched to next song \n " ) ;
status_update ( PLAY_PLAYING ) ;
2011-03-26 13:33:19 -04:00
metadata_prune ( pos ) ;
2010-04-04 06:43:15 -04:00
}
return pos ;
}
2016-11-08 15:27:38 -05:00
/*
* Returns the next player source based on the current streaming source and repeat mode
*
* If repeat mode is repeat all , shuffle is active and the current streaming source is the
* last item in the queue , the queue is reshuffled prior to returning the first item of the
* queue .
*/
static struct player_source *
source_next ( )
{
struct player_source * ps = NULL ;
struct db_queue_item * queue_item ;
if ( ! cur_streaming )
{
DPRINTF ( E_LOG , L_PLAYER , " source_next() called with no current streaming source available \n " ) ;
return NULL ;
}
if ( repeat = = REPEAT_SONG )
{
queue_item = db_queue_fetch_byitemid ( cur_streaming - > item_id ) ;
if ( ! queue_item )
{
DPRINTF ( E_LOG , L_PLAYER , " Error fetching item from queue '%s' (id=%d, item-id=%d) \n " , cur_streaming - > path , cur_streaming - > id , cur_streaming - > item_id ) ;
return NULL ;
}
}
else
{
queue_item = db_queue_fetch_next ( cur_streaming - > item_id , shuffle ) ;
2016-11-12 12:45:54 -05:00
if ( ! queue_item & & repeat = = REPEAT_ALL )
2016-11-08 15:27:38 -05:00
{
if ( shuffle )
{
db_queue_reshuffle ( 0 ) ;
}
queue_item = db_queue_fetch_bypos ( 0 , shuffle ) ;
if ( ! queue_item )
{
DPRINTF ( E_LOG , L_PLAYER , " Error fetching item from queue '%s' (id=%d, item-id=%d) \n " , cur_streaming - > path , cur_streaming - > id , cur_streaming - > item_id ) ;
return NULL ;
}
}
2016-11-12 12:45:54 -05:00
}
2016-11-08 15:27:38 -05:00
2016-11-12 12:45:54 -05:00
if ( ! queue_item )
{
DPRINTF ( E_DBG , L_PLAYER , " Reached end of queue \n " ) ;
return NULL ;
2016-11-08 15:27:38 -05:00
}
2016-11-12 12:45:54 -05:00
ps = source_new ( queue_item ) ;
free_queue_item ( queue_item , 0 ) ;
2016-11-08 15:27:38 -05:00
return ps ;
}
/*
* Returns the previous player source based on the current streaming source
*/
static struct player_source *
source_prev ( )
{
struct player_source * ps = NULL ;
struct db_queue_item * queue_item ;
if ( ! cur_streaming )
{
DPRINTF ( E_LOG , L_PLAYER , " source_prev() called with no current streaming source available \n " ) ;
return NULL ;
}
queue_item = db_queue_fetch_prev ( cur_streaming - > item_id , shuffle ) ;
if ( ! queue_item )
return NULL ;
ps = source_new ( queue_item ) ;
free_queue_item ( queue_item , 0 ) ;
return ps ;
}
2010-12-12 04:00:43 -05:00
static int
2010-04-04 06:43:15 -04:00
source_read ( uint8_t * buf , int len , uint64_t rtptime )
{
int ret ;
int nbytes ;
2015-08-08 12:02:49 -04:00
char * silence_buf ;
2016-11-08 15:27:38 -05:00
struct player_source * ps ;
2010-04-04 06:43:15 -04:00
if ( ! cur_streaming )
2010-12-12 04:00:43 -05:00
return 0 ;
2010-04-04 06:43:15 -04:00
nbytes = 0 ;
while ( nbytes < len )
{
2014-09-28 16:09:22 -04:00
if ( evbuffer_get_length ( audio_buf ) = = 0 )
2010-04-04 06:43:15 -04:00
{
2015-08-08 12:02:49 -04:00
if ( cur_streaming )
2014-03-11 18:20:29 -04:00
{
2015-09-02 12:31:43 -04:00
ret = stream_read ( cur_streaming , len - nbytes ) ;
2015-08-08 12:02:49 -04:00
}
2015-10-19 15:33:43 -04:00
else if ( cur_playing )
2015-08-08 12:02:49 -04:00
{
// Reached end of playlist (cur_playing is NULL) send silence and source_check will abort playback if the last item was played
2015-09-02 12:31:43 -04:00
DPRINTF ( E_SPAM , L_PLAYER , " End of playlist reached, stream silence until playback of last item ends \n " ) ;
2015-08-08 12:02:49 -04:00
silence_buf = ( char * ) calloc ( ( len - nbytes ) , sizeof ( char ) ) ;
evbuffer_add ( audio_buf , silence_buf , ( len - nbytes ) ) ;
free ( silence_buf ) ;
ret = len - nbytes ;
2014-03-11 18:20:29 -04:00
}
2015-10-19 15:33:43 -04:00
else
{
// If cur_streaming and cur_playing are NULL, source_read for all queue items failed. Playback will be aborted in the calling function
return - 1 ;
}
2015-09-18 14:01:20 -04:00
2010-04-04 06:43:15 -04:00
if ( ret < = 0 )
{
/* EOF or error */
2015-09-02 12:31:43 -04:00
source_close ( rtptime + BTOS ( nbytes ) - 1 ) ;
2010-04-04 06:43:15 -04:00
2015-09-18 14:01:20 -04:00
DPRINTF ( E_DBG , L_PLAYER , " New file \n " ) ;
2016-11-08 15:27:38 -05:00
ps = source_next ( ) ;
2015-09-18 14:01:20 -04:00
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Error reading source %d \n " , cur_streaming - > id ) ;
2016-11-08 15:27:38 -05:00
db_queue_delete_byitemid ( cur_streaming - > item_id ) ;
2015-09-18 14:01:20 -04:00
}
2016-11-08 15:27:38 -05:00
if ( ps )
2015-09-18 14:01:20 -04:00
{
2016-11-08 15:27:38 -05:00
ret = source_open ( ps , cur_streaming - > end + 1 , 0 ) ;
2015-09-18 14:01:20 -04:00
if ( ret < 0 )
return - 1 ;
ret = source_play ( ) ;
if ( ret < 0 )
return - 1 ;
2015-09-26 02:45:52 -04:00
metadata_trigger ( 0 ) ;
2015-09-18 14:01:20 -04:00
}
else
{
cur_streaming = NULL ;
}
2010-04-04 06:43:15 -04:00
continue ;
}
}
nbytes + = evbuffer_remove ( audio_buf , buf + nbytes , len - nbytes ) ;
}
2010-12-12 04:00:43 -05:00
return nbytes ;
2010-04-04 06:43:15 -04:00
}
static void
2016-04-06 02:18:54 -04:00
playback_write ( int read_skip )
2010-04-04 06:43:15 -04:00
{
2010-12-12 04:00:43 -05:00
int ret ;
2010-04-04 06:43:15 -04:00
source_check ( ) ;
2015-09-02 12:31:43 -04:00
2010-04-04 06:43:15 -04:00
/* Make sure playback is still running after source_check() */
if ( player_state = = PLAY_STOPPED )
return ;
last_rtptime + = AIRTUNES_V2_PACKET_SAMPLES ;
2016-04-06 02:18:54 -04:00
if ( ! read_skip )
2010-12-12 04:00:43 -05:00
{
2016-04-06 02:18:54 -04:00
ret = source_read ( rawbuf , sizeof ( rawbuf ) , last_rtptime ) ;
if ( ret < 0 )
{
DPRINTF ( E_DBG , L_PLAYER , " Error reading from source, aborting playback \n " ) ;
2010-12-12 04:00:43 -05:00
2016-04-06 02:18:54 -04:00
playback_abort ( ) ;
return ;
}
2010-12-12 04:00:43 -05:00
}
2016-04-06 02:18:54 -04:00
else
DPRINTF ( E_SPAM , L_PLAYER , " Skipping read \n " ) ;
2010-04-04 06:43:15 -04:00
2016-03-22 17:59:50 -04:00
outputs_write ( rawbuf , last_rtptime ) ;
2010-05-01 13:24:06 -04:00
}
static void
player_playback_cb ( int fd , short what , void * arg )
{
2011-02-17 10:21:35 -05:00
struct timespec next_tick ;
2016-02-11 14:02:17 -05:00
uint64_t overrun ;
2016-02-11 02:29:29 -05:00
int ret ;
2016-04-06 02:18:54 -04:00
int skip ;
int skip_first ;
2010-05-01 13:24:06 -04:00
2016-02-11 02:29:29 -05:00
// Check if we missed any timer expirations
2016-02-11 14:02:17 -05:00
overrun = 0 ;
2016-02-11 02:29:29 -05:00
# if defined(__linux__)
2016-02-11 14:02:17 -05:00
ret = read ( fd , & overrun , sizeof ( overrun ) ) ;
2015-02-28 02:00:10 -05:00
if ( ret < = 0 )
2016-02-11 02:29:29 -05:00
DPRINTF ( E_LOG , L_PLAYER , " Error reading timer \n " ) ;
2016-02-11 14:02:17 -05:00
else if ( overrun > 0 )
overrun - - ;
2016-02-11 02:29:29 -05:00
# else
ret = timer_getoverrun ( pb_timer ) ;
if ( ret < 0 )
DPRINTF ( E_LOG , L_PLAYER , " Error getting timer overrun \n " ) ;
else
2016-02-11 14:02:17 -05:00
overrun = ret ;
2014-09-27 16:59:19 -04:00
# endif /* __linux__ */
2010-05-01 13:24:06 -04:00
2016-02-11 02:29:29 -05:00
// The reason we get behind the playback timer may be that we are playing a
2016-02-28 16:28:37 -05:00
// network stream OR that the source is slow to open OR some interruption.
// For streams, we might be consuming faster than the stream delivers, so
2016-02-11 02:29:29 -05:00
// when ffmpeg's buffer empties (might take a few hours) our av_read_frame()
// in transcode.c will begin to block, because ffmpeg has to wait for new data
2016-02-28 16:28:37 -05:00
// from the stream server.
//
// Our strategy to catch up with the timer depends on the source:
2016-04-06 02:18:54 -04:00
// - streams: We will skip reading data every second until we have countered
// the overrun by skipping reads for a number of ticks that is
// 3 times the overrun. That should make the source catch up. To
// keep the output happy we resend the previous rawbuf when we
// have skipped a read.
2016-02-28 16:28:37 -05:00
// - files: Just read and write like crazy until we have caught up.
2016-04-06 02:18:54 -04:00
skip_first = 0 ;
2016-02-11 14:02:17 -05:00
if ( overrun > PLAYER_TICKS_MAX_OVERRUN )
2016-02-11 02:29:29 -05:00
{
2016-09-25 16:19:59 -04:00
DPRINTF ( E_WARN , L_PLAYER , " Behind the playback timer with % " PRIu64 " ticks \n " , overrun ) ;
2016-02-28 16:28:37 -05:00
2016-09-25 16:19:59 -04:00
if ( cur_streaming & & ( cur_streaming - > data_kind = = DATA_KIND_HTTP | | cur_streaming - > data_kind = = DATA_KIND_PIPE ) )
2016-04-06 02:18:54 -04:00
{
ticks_skip = 3 * overrun ;
2016-09-25 16:19:59 -04:00
DPRINTF ( E_WARN , L_PLAYER , " Will skip reading for a total of %d ticks to catch up \n " , ticks_skip ) ;
2016-04-06 02:18:54 -04:00
// We always skip after a timer overrun, since another read will
// probably just give another time overrun
skip_first = 1 ;
}
2016-03-06 04:55:10 -05:00
else
ticks_skip = 0 ;
2016-02-11 02:29:29 -05:00
}
// Decide how many packets to send
next_tick = timespec_add ( pb_timer_last , tick_interval ) ;
2016-02-11 14:02:17 -05:00
for ( ; overrun > 0 ; overrun - - )
2016-02-11 02:29:29 -05:00
next_tick = timespec_add ( next_tick , tick_interval ) ;
2011-02-17 10:21:35 -05:00
do
{
2016-04-06 02:18:54 -04:00
skip = skip_first | | ( ( ticks_skip > 0 ) & & ( ( last_rtptime / AIRTUNES_V2_PACKET_SAMPLES ) % 126 = = 0 ) ) ;
playback_write ( skip ) ;
skip_first = 0 ;
if ( skip )
ticks_skip - - ;
2016-02-11 02:29:29 -05:00
2011-02-17 10:21:35 -05:00
packet_timer_last = timespec_add ( packet_timer_last , packet_time ) ;
}
2016-04-14 02:55:59 -04:00
while ( ( timespec_cmp ( packet_timer_last , next_tick ) < 0 ) & & ( player_state = = PLAY_PLAYING ) ) ;
2010-04-04 06:43:15 -04:00
2010-05-09 03:24:05 -04:00
/* Make sure playback is still running */
if ( player_state = = PLAY_STOPPED )
return ;
2016-02-11 02:29:29 -05:00
pb_timer_last = next_tick ;
2010-05-01 13:24:06 -04:00
}
2010-04-04 06:43:15 -04:00
2010-11-19 14:27:18 -05:00
/* Helpers */
2016-04-04 15:54:37 -04:00
static void
device_list_sort ( void )
{
struct output_device * device ;
struct output_device * next ;
struct output_device * prev ;
int swaps ;
// Swap sorting since even the most inefficient sorting should do fine here
do
{
swaps = 0 ;
prev = NULL ;
for ( device = dev_list ; device & & device - > next ; device = device - > next )
{
next = device - > next ;
if ( ( outputs_priority ( device ) > outputs_priority ( next ) ) | |
( outputs_priority ( device ) = = outputs_priority ( next ) & & strcasecmp ( device - > name , next - > name ) > 0 ) )
{
if ( device = = dev_list )
dev_list = next ;
if ( prev )
prev - > next = next ;
device - > next = next - > next ;
next - > next = device ;
swaps + + ;
}
prev = device ;
}
}
while ( swaps > 0 ) ;
}
2010-04-04 06:43:15 -04:00
static void
2016-01-23 19:14:07 -05:00
device_remove ( struct output_device * remove )
2010-04-04 06:43:15 -04:00
{
2016-01-23 19:14:07 -05:00
struct output_device * device ;
struct output_device * prev ;
2010-11-19 14:48:33 -05:00
int ret ;
2010-04-04 06:43:15 -04:00
prev = NULL ;
2016-01-23 19:14:07 -05:00
for ( device = dev_list ; device ; device = device - > next )
2010-04-04 06:43:15 -04:00
{
2016-01-23 19:14:07 -05:00
if ( device = = remove )
2010-04-04 06:43:15 -04:00
break ;
2016-01-23 19:14:07 -05:00
prev = device ;
2010-04-04 06:43:15 -04:00
}
2016-01-23 19:14:07 -05:00
if ( ! device )
2010-04-04 06:43:15 -04:00
return ;
2016-04-30 17:55:45 -04:00
/* Save device volume */
ret = db_speaker_save ( remove - > id , remove - > selected , remove - > volume , remove - > name ) ;
if ( ret < 0 )
DPRINTF ( E_LOG , L_PLAYER , " Could not save state for %s device '%s' \n " , remove - > type_name , remove - > name ) ;
2016-01-23 19:14:07 -05:00
DPRINTF ( E_DBG , L_PLAYER , " Removing %s device '%s'; stopped advertising \n " , remove - > type_name , remove - > name ) ;
2010-04-04 06:43:15 -04:00
2010-11-21 04:54:55 -05:00
/* Make sure device isn't selected anymore */
2016-04-30 17:55:45 -04:00
if ( remove - > selected )
2016-01-23 19:14:07 -05:00
speaker_deselect_output ( remove ) ;
2010-11-21 04:54:55 -05:00
2010-04-04 06:43:15 -04:00
if ( ! prev )
2016-01-23 19:14:07 -05:00
dev_list = remove - > next ;
2010-04-04 06:43:15 -04:00
else
2016-01-23 19:14:07 -05:00
prev - > next = remove - > next ;
2010-04-04 06:43:15 -04:00
2016-01-23 19:14:07 -05:00
outputs_device_free ( remove ) ;
2010-04-04 06:43:15 -04:00
}
static int
2016-01-23 19:14:07 -05:00
device_check ( struct output_device * check )
2010-04-04 06:43:15 -04:00
{
2016-01-23 19:14:07 -05:00
struct output_device * device ;
2010-04-04 06:43:15 -04:00
2016-01-23 19:14:07 -05:00
for ( device = dev_list ; device ; device = device - > next )
2010-04-04 06:43:15 -04:00
{
2016-01-23 19:14:07 -05:00
if ( device = = check )
2010-04-04 06:43:15 -04:00
break ;
}
2016-01-23 19:14:07 -05:00
return ( device ) ? 0 : - 1 ;
2010-04-04 06:43:15 -04:00
}
2016-05-15 00:25:56 -04:00
static enum command_state
device_add ( void * arg , int * retval )
2010-11-19 13:13:30 -05:00
{
2016-05-15 00:25:56 -04:00
union player_arg * cmdarg ;
2016-01-23 19:14:07 -05:00
struct output_device * add ;
struct output_device * device ;
2010-11-19 14:48:33 -05:00
int selected ;
2010-11-19 14:20:37 -05:00
int ret ;
2016-05-15 00:25:56 -04:00
cmdarg = arg ;
add = cmdarg - > device ;
2010-11-19 14:20:37 -05:00
2016-01-23 19:14:07 -05:00
for ( device = dev_list ; device ; device = device - > next )
2010-11-19 14:20:37 -05:00
{
2016-01-23 19:14:07 -05:00
if ( device - > id = = add - > id )
2010-11-19 14:20:37 -05:00
break ;
}
/* New device */
2016-01-23 19:14:07 -05:00
if ( ! device )
2010-11-19 14:20:37 -05:00
{
2016-01-23 19:14:07 -05:00
device = add ;
2010-11-19 14:20:37 -05:00
2016-01-23 19:14:07 -05:00
ret = db_speaker_get ( device - > id , & selected , & device - > volume ) ;
2010-11-19 14:48:33 -05:00
if ( ret < 0 )
2010-11-19 16:51:46 -05:00
{
selected = 0 ;
2016-01-23 19:14:07 -05:00
device - > volume = ( master_volume > = 0 ) ? master_volume : PLAYER_DEFAULT_VOLUME ;
2010-11-19 16:51:46 -05:00
}
2010-11-19 15:22:55 -05:00
2016-04-30 17:55:45 -04:00
if ( selected & & ( player_state ! = PLAY_PLAYING ) )
2016-01-23 19:14:07 -05:00
speaker_select_output ( device ) ;
2010-11-19 14:20:37 -05:00
2016-01-23 19:14:07 -05:00
device - > next = dev_list ;
dev_list = device ;
2010-11-19 14:20:37 -05:00
}
2016-01-23 19:14:07 -05:00
// Update to a device already in the list
2010-11-19 14:20:37 -05:00
else
{
2016-01-23 19:14:07 -05:00
device - > advertised = 1 ;
2010-11-19 14:20:37 -05:00
2016-01-23 19:14:07 -05:00
if ( add - > v4_address )
2010-11-19 14:20:37 -05:00
{
2016-01-23 19:14:07 -05:00
if ( device - > v4_address )
free ( device - > v4_address ) ;
2010-11-19 14:20:37 -05:00
2016-01-23 19:14:07 -05:00
device - > v4_address = add - > v4_address ;
device - > v4_port = add - > v4_port ;
2011-03-15 14:18:45 -04:00
/* Address is ours now */
2016-01-23 19:14:07 -05:00
add - > v4_address = NULL ;
2010-11-19 14:20:37 -05:00
}
2016-01-23 19:14:07 -05:00
if ( add - > v6_address )
2010-11-19 14:20:37 -05:00
{
2016-01-23 19:14:07 -05:00
if ( device - > v6_address )
free ( device - > v6_address ) ;
2010-11-19 14:20:37 -05:00
2016-01-23 19:14:07 -05:00
device - > v6_address = add - > v6_address ;
device - > v6_port = add - > v6_port ;
2011-03-15 14:18:45 -04:00
/* Address is ours now */
2016-01-23 19:14:07 -05:00
add - > v6_address = NULL ;
2010-11-19 14:20:37 -05:00
}
2016-01-23 19:14:07 -05:00
if ( device - > name )
free ( device - > name ) ;
device - > name = add - > name ;
add - > name = NULL ;
2010-11-19 14:20:37 -05:00
2016-01-23 19:14:07 -05:00
device - > has_password = add - > has_password ;
device - > password = add - > password ;
2010-11-19 14:20:37 -05:00
2016-01-23 19:14:07 -05:00
outputs_device_free ( add ) ;
2010-11-19 14:20:37 -05:00
}
2016-04-04 15:54:37 -04:00
device_list_sort ( ) ;
2016-05-15 00:25:56 -04:00
* retval = 0 ;
return COMMAND_END ;
2010-11-19 14:20:37 -05:00
}
2016-05-15 00:25:56 -04:00
static enum command_state
device_remove_family ( void * arg , int * retval )
2010-11-19 14:20:37 -05:00
{
2016-05-15 00:25:56 -04:00
union player_arg * cmdarg ;
2016-01-23 19:14:07 -05:00
struct output_device * remove ;
struct output_device * device ;
2010-11-19 13:13:30 -05:00
2016-05-15 00:25:56 -04:00
cmdarg = arg ;
remove = cmdarg - > device ;
2010-11-19 14:20:37 -05:00
2016-01-23 19:14:07 -05:00
for ( device = dev_list ; device ; device = device - > next )
2010-11-19 13:13:30 -05:00
{
2016-01-23 19:14:07 -05:00
if ( device - > id = = remove - > id )
2010-11-19 13:13:30 -05:00
break ;
}
2016-01-23 19:14:07 -05:00
if ( ! device )
2010-11-19 13:13:30 -05:00
{
2016-01-23 19:14:07 -05:00
DPRINTF ( E_WARN , L_PLAYER , " The %s device '%s' stopped advertising, but not in our list \n " , remove - > type_name , remove - > name ) ;
2010-11-19 13:13:30 -05:00
2016-01-23 19:14:07 -05:00
outputs_device_free ( remove ) ;
2016-05-15 00:25:56 -04:00
* retval = 0 ;
return COMMAND_END ;
2010-11-19 13:13:30 -05:00
}
2010-11-19 14:20:37 -05:00
/* v{4,6}_port non-zero indicates the address family stopped advertising */
2016-01-23 19:14:07 -05:00
if ( remove - > v4_port & & device - > v4_address )
2010-11-19 13:13:30 -05:00
{
2016-01-23 19:14:07 -05:00
free ( device - > v4_address ) ;
device - > v4_address = NULL ;
device - > v4_port = 0 ;
2010-11-19 14:20:37 -05:00
}
2010-11-19 13:13:30 -05:00
2016-01-23 19:14:07 -05:00
if ( remove - > v6_port & & device - > v6_address )
2010-11-19 14:20:37 -05:00
{
2016-01-23 19:14:07 -05:00
free ( device - > v6_address ) ;
device - > v6_address = NULL ;
device - > v6_port = 0 ;
2010-11-19 13:13:30 -05:00
}
2016-01-23 19:14:07 -05:00
if ( ! device - > v4_address & & ! device - > v6_address )
2010-11-19 13:13:30 -05:00
{
2016-01-23 19:14:07 -05:00
device - > advertised = 0 ;
2010-11-19 13:13:30 -05:00
2016-01-23 19:14:07 -05:00
if ( ! device - > session )
device_remove ( device ) ;
2010-11-19 14:20:37 -05:00
}
2010-11-19 13:13:30 -05:00
2016-01-23 19:14:07 -05:00
outputs_device_free ( remove ) ;
2010-11-19 13:13:30 -05:00
2016-05-15 00:25:56 -04:00
* retval = 0 ;
return COMMAND_END ;
2010-11-19 13:13:30 -05:00
}
2016-05-15 00:25:56 -04:00
static enum command_state
metadata_send ( void * arg , int * retval )
2015-04-07 17:35:56 -04:00
{
2016-05-15 00:25:56 -04:00
union player_arg * cmdarg ;
2015-04-09 15:04:35 -04:00
struct player_metadata * pmd ;
2015-04-07 17:35:56 -04:00
2016-05-15 00:25:56 -04:00
cmdarg = arg ;
pmd = cmdarg - > pmd ;
2015-04-07 17:35:56 -04:00
2015-04-09 15:04:35 -04:00
/* Do the setting of rtptime which was deferred in metadata_trigger because we
* wanted to wait until we had the actual last_rtptime
*/
if ( ( pmd - > rtptime = = 0 ) & & ( pmd - > startup ) )
pmd - > rtptime = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES ;
2016-01-23 19:14:07 -05:00
outputs_metadata_send ( pmd - > omd , pmd - > rtptime , pmd - > offset , pmd - > startup ) ;
2015-04-07 17:35:56 -04:00
2016-05-15 00:25:56 -04:00
* retval = 0 ;
return COMMAND_END ;
2015-04-07 17:35:56 -04:00
}
2016-01-23 19:14:07 -05:00
/* Output device callbacks executed in the player thread */
2010-04-04 06:43:15 -04:00
static void
2016-01-23 19:14:07 -05:00
device_streaming_cb ( struct output_device * device , struct output_session * session , enum output_device_state status )
2010-04-04 06:43:15 -04:00
{
int ret ;
2016-04-04 10:58:07 -04:00
DPRINTF ( E_DBG , L_PLAYER , " Callback from %s to device_streaming_cb \n " , outputs_name ( device - > type ) ) ;
2016-02-02 16:37:08 -05:00
ret = device_check ( device ) ;
if ( ret < 0 )
2010-04-04 06:43:15 -04:00
{
2016-02-02 16:37:08 -05:00
DPRINTF ( E_LOG , L_PLAYER , " Output device disappeared during streaming! \n " ) ;
2010-04-04 06:43:15 -04:00
2016-02-02 16:37:08 -05:00
output_sessions - - ;
return ;
}
2010-04-04 06:43:15 -04:00
2016-02-02 16:37:08 -05:00
if ( status = = OUTPUT_STATE_FAILED )
{
2016-01-23 19:14:07 -05:00
DPRINTF ( E_LOG , L_PLAYER , " The %s device '%s' FAILED \n " , device - > type_name , device - > name ) ;
2010-04-04 06:43:15 -04:00
2016-02-02 16:37:08 -05:00
output_sessions - - ;
2010-04-04 06:43:15 -04:00
if ( player_state = = PLAY_PLAYING )
2016-01-23 19:14:07 -05:00
speaker_deselect_output ( device ) ;
2010-04-04 06:43:15 -04:00
2016-01-23 19:14:07 -05:00
device - > session = NULL ;
2010-04-04 06:43:15 -04:00
2016-01-23 19:14:07 -05:00
if ( ! device - > advertised )
device_remove ( device ) ;
2016-02-02 16:37:08 -05:00
if ( output_sessions = = 0 )
playback_abort ( ) ;
2010-04-04 06:43:15 -04:00
}
2016-01-23 19:14:07 -05:00
else if ( status = = OUTPUT_STATE_STOPPED )
2010-04-04 06:43:15 -04:00
{
2016-01-23 19:14:07 -05:00
DPRINTF ( E_INFO , L_PLAYER , " The %s device '%s' stopped \n " , device - > type_name , device - > name ) ;
2010-04-04 06:43:15 -04:00
2016-02-02 16:37:08 -05:00
output_sessions - - ;
2016-01-23 19:14:07 -05:00
device - > session = NULL ;
2010-04-04 06:43:15 -04:00
2016-01-23 19:14:07 -05:00
if ( ! device - > advertised )
device_remove ( device ) ;
2010-04-04 06:43:15 -04:00
}
2016-02-02 06:02:14 -05:00
else
outputs_status_cb ( session , device_streaming_cb ) ;
2010-04-04 06:43:15 -04:00
}
static void
2016-01-23 19:14:07 -05:00
device_command_cb ( struct output_device * device , struct output_session * session , enum output_device_state status )
2010-04-04 06:43:15 -04:00
{
2016-04-04 10:58:07 -04:00
DPRINTF ( E_DBG , L_PLAYER , " Callback from %s to device_command_cb \n " , outputs_name ( device - > type ) ) ;
2016-01-23 19:14:07 -05:00
outputs_status_cb ( session , device_streaming_cb ) ;
2010-04-04 06:43:15 -04:00
2016-01-23 19:14:07 -05:00
if ( status = = OUTPUT_STATE_FAILED )
device_streaming_cb ( device , session , status ) ;
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
commands_exec_end ( cmdbase , 0 ) ;
2010-04-04 06:43:15 -04:00
}
static void
2016-01-23 19:14:07 -05:00
device_shutdown_cb ( struct output_device * device , struct output_session * session , enum output_device_state status )
2010-04-04 06:43:15 -04:00
{
2016-05-15 00:25:56 -04:00
int retval ;
2010-04-04 06:43:15 -04:00
int ret ;
2016-04-04 10:58:07 -04:00
DPRINTF ( E_DBG , L_PLAYER , " Callback from %s to device_shutdown_cb \n " , outputs_name ( device - > type ) ) ;
2016-01-23 19:14:07 -05:00
if ( output_sessions )
output_sessions - - ;
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
retval = commands_exec_returnvalue ( cmdbase ) ;
2016-01-23 19:14:07 -05:00
ret = device_check ( device ) ;
2010-04-04 06:43:15 -04:00
if ( ret < 0 )
{
2016-01-23 19:14:07 -05:00
DPRINTF ( E_WARN , L_PLAYER , " Output device disappeared before shutdown completion! \n " ) ;
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
if ( retval ! = - 2 )
retval = - 1 ;
2010-04-04 06:43:15 -04:00
goto out ;
}
2016-01-23 19:14:07 -05:00
device - > session = NULL ;
2010-04-04 06:43:15 -04:00
2016-01-23 19:14:07 -05:00
if ( ! device - > advertised )
device_remove ( device ) ;
2010-04-04 06:43:15 -04:00
out :
2016-05-15 00:25:56 -04:00
/* cur_cmd->ret already set
* - to 0 ( or - 2 if password issue ) in speaker_set ( )
* - to - 1 above on error
*/
commands_exec_end ( cmdbase , retval ) ;
2010-04-04 06:43:15 -04:00
}
static void
2016-01-23 19:14:07 -05:00
device_lost_cb ( struct output_device * device , struct output_session * session , enum output_device_state status )
2010-04-04 06:43:15 -04:00
{
2016-04-04 10:58:07 -04:00
DPRINTF ( E_DBG , L_PLAYER , " Callback from %s to device_lost_cb \n " , outputs_name ( device - > type ) ) ;
2010-04-04 06:43:15 -04:00
/* We lost that device during startup for some reason, not much we can do here */
2016-01-23 19:14:07 -05:00
if ( status = = OUTPUT_STATE_FAILED )
2010-04-04 06:43:15 -04:00
DPRINTF ( E_WARN , L_PLAYER , " Failed to stop lost device \n " ) ;
else
DPRINTF ( E_INFO , L_PLAYER , " Lost device stopped properly \n " ) ;
}
static void
2016-01-23 19:14:07 -05:00
device_activate_cb ( struct output_device * device , struct output_session * session , enum output_device_state status )
2010-04-04 06:43:15 -04:00
{
struct timespec ts ;
2016-05-15 00:25:56 -04:00
int retval ;
2010-04-04 06:43:15 -04:00
int ret ;
2016-04-04 10:58:07 -04:00
DPRINTF ( E_DBG , L_PLAYER , " Callback from %s to device_activate_cb \n " , outputs_name ( device - > type ) ) ;
2016-05-15 00:25:56 -04:00
retval = commands_exec_returnvalue ( cmdbase ) ;
2016-01-23 19:14:07 -05:00
ret = device_check ( device ) ;
2010-04-04 06:43:15 -04:00
if ( ret < 0 )
{
2016-01-23 19:14:07 -05:00
DPRINTF ( E_WARN , L_PLAYER , " Output device disappeared during startup! \n " ) ;
2010-04-04 06:43:15 -04:00
2016-01-23 19:14:07 -05:00
outputs_status_cb ( session , device_lost_cb ) ;
outputs_device_stop ( session ) ;
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
if ( retval ! = - 2 )
retval = - 1 ;
2010-04-04 06:43:15 -04:00
goto out ;
}
2016-01-23 19:14:07 -05:00
if ( status = = OUTPUT_STATE_PASSWORD )
2010-04-04 06:43:15 -04:00
{
2016-01-23 19:14:07 -05:00
status = OUTPUT_STATE_FAILED ;
2016-05-15 00:25:56 -04:00
retval = - 2 ;
2010-04-04 06:43:15 -04:00
}
2016-01-23 19:14:07 -05:00
if ( status = = OUTPUT_STATE_FAILED )
2010-04-04 06:43:15 -04:00
{
2016-01-23 19:14:07 -05:00
speaker_deselect_output ( device ) ;
2010-04-04 06:43:15 -04:00
2016-01-23 19:14:07 -05:00
if ( ! device - > advertised )
device_remove ( device ) ;
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
if ( retval ! = - 2 )
retval = - 1 ;
2010-04-04 06:43:15 -04:00
goto out ;
}
2016-01-23 19:14:07 -05:00
device - > session = session ;
2010-04-04 06:43:15 -04:00
2016-01-23 19:14:07 -05:00
output_sessions + + ;
2010-04-04 06:43:15 -04:00
2016-01-23 19:14:07 -05:00
if ( ( player_state = = PLAY_PLAYING ) & & ( output_sessions = = 1 ) )
2010-04-04 06:43:15 -04:00
{
2011-02-17 10:21:35 -05:00
ret = clock_gettime_with_res ( CLOCK_MONOTONIC , & ts , & timer_res ) ;
2010-04-04 06:43:15 -04:00
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Could not get current time: %s \n " , strerror ( errno ) ) ;
/* Fallback to nearest timer expiration time */
ts . tv_sec = pb_timer_last . tv_sec ;
ts . tv_nsec = pb_timer_last . tv_nsec ;
}
2016-01-23 19:14:07 -05:00
outputs_playback_start ( last_rtptime + AIRTUNES_V2_PACKET_SAMPLES , & ts ) ;
2010-04-04 06:43:15 -04:00
}
2016-01-23 19:14:07 -05:00
outputs_status_cb ( session , device_streaming_cb ) ;
2010-04-04 06:43:15 -04:00
out :
2016-05-15 00:25:56 -04:00
/* cur_cmd->ret already set
* - to 0 in speaker_set ( ) ( default )
* - to - 2 above if password issue
* - to - 1 above on error
*/
commands_exec_end ( cmdbase , retval ) ;
2010-04-04 06:43:15 -04:00
}
static void
2016-01-23 19:14:07 -05:00
device_probe_cb ( struct output_device * device , struct output_session * session , enum output_device_state status )
2010-04-04 06:43:15 -04:00
{
2016-05-15 00:25:56 -04:00
int retval ;
2010-04-04 06:43:15 -04:00
int ret ;
2016-04-04 10:58:07 -04:00
DPRINTF ( E_DBG , L_PLAYER , " Callback from %s to device_probe_cb \n " , outputs_name ( device - > type ) ) ;
2016-05-15 00:25:56 -04:00
retval = commands_exec_returnvalue ( cmdbase ) ;
2016-01-23 19:14:07 -05:00
ret = device_check ( device ) ;
2010-04-04 06:43:15 -04:00
if ( ret < 0 )
{
2016-01-23 19:14:07 -05:00
DPRINTF ( E_WARN , L_PLAYER , " Output device disappeared during probe! \n " ) ;
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
if ( retval ! = - 2 )
retval = - 1 ;
2010-04-04 06:43:15 -04:00
goto out ;
}
2016-01-23 19:14:07 -05:00
if ( status = = OUTPUT_STATE_PASSWORD )
2010-04-04 06:43:15 -04:00
{
2016-01-23 19:14:07 -05:00
status = OUTPUT_STATE_FAILED ;
2016-05-15 00:25:56 -04:00
retval = - 2 ;
2010-04-04 06:43:15 -04:00
}
2016-01-23 19:14:07 -05:00
if ( status = = OUTPUT_STATE_FAILED )
2010-04-04 06:43:15 -04:00
{
2016-01-23 19:14:07 -05:00
speaker_deselect_output ( device ) ;
2010-04-04 06:43:15 -04:00
2016-01-23 19:14:07 -05:00
if ( ! device - > advertised )
device_remove ( device ) ;
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
if ( retval ! = - 2 )
retval = - 1 ;
2010-04-04 06:43:15 -04:00
goto out ;
}
out :
2016-05-15 00:25:56 -04:00
/* cur_cmd->ret already set
* - to 0 in speaker_set ( ) ( default )
* - to - 2 above if password issue
* - to - 1 above on error
*/
commands_exec_end ( cmdbase , retval ) ;
2010-04-04 06:43:15 -04:00
}
static void
2016-01-23 19:14:07 -05:00
device_restart_cb ( struct output_device * device , struct output_session * session , enum output_device_state status )
2010-04-04 06:43:15 -04:00
{
int ret ;
2016-04-04 10:58:07 -04:00
DPRINTF ( E_DBG , L_PLAYER , " Callback from %s to device_restart_cb \n " , outputs_name ( device - > type ) ) ;
2016-01-23 19:14:07 -05:00
ret = device_check ( device ) ;
2010-04-04 06:43:15 -04:00
if ( ret < 0 )
{
2016-01-23 19:14:07 -05:00
DPRINTF ( E_WARN , L_PLAYER , " Output device disappeared during restart! \n " ) ;
2010-04-04 06:43:15 -04:00
2016-01-23 19:14:07 -05:00
outputs_status_cb ( session , device_lost_cb ) ;
outputs_device_stop ( session ) ;
2010-04-04 06:43:15 -04:00
goto out ;
}
2016-01-23 19:14:07 -05:00
if ( status = = OUTPUT_STATE_FAILED )
2010-04-04 06:43:15 -04:00
{
2016-01-23 19:14:07 -05:00
speaker_deselect_output ( device ) ;
2010-04-04 06:43:15 -04:00
2016-01-23 19:14:07 -05:00
if ( ! device - > advertised )
device_remove ( device ) ;
2010-04-04 06:43:15 -04:00
goto out ;
}
2016-01-23 19:14:07 -05:00
device - > session = session ;
2010-04-04 06:43:15 -04:00
2016-01-23 19:14:07 -05:00
output_sessions + + ;
outputs_status_cb ( session , device_streaming_cb ) ;
2010-04-04 06:43:15 -04:00
out :
2016-05-15 00:25:56 -04:00
commands_exec_end ( cmdbase , 0 ) ;
2010-04-04 06:43:15 -04:00
}
2010-12-12 04:12:39 -05:00
/* Internal abort routine */
static void
playback_abort ( void )
{
2016-03-22 17:59:50 -04:00
outputs_playback_stop ( ) ;
2010-12-12 04:12:39 -05:00
2014-09-27 16:59:19 -04:00
pb_timer_stop ( ) ;
2010-12-12 04:12:39 -05:00
2015-09-02 12:31:43 -04:00
source_stop ( ) ;
2010-12-12 04:12:39 -05:00
2014-09-28 16:09:22 -04:00
evbuffer_drain ( audio_buf , evbuffer_get_length ( audio_buf ) ) ;
2010-12-12 04:12:39 -05:00
2016-02-28 04:34:18 -05:00
if ( ! clear_queue_on_stop_disabled )
2016-11-08 15:27:38 -05:00
db_queue_clear ( ) ;
2016-02-28 04:34:18 -05:00
2010-12-12 04:12:39 -05:00
status_update ( PLAY_STOPPED ) ;
2011-03-26 13:33:19 -04:00
metadata_purge ( ) ;
2010-12-12 04:12:39 -05:00
}
2010-04-04 06:43:15 -04:00
/* Actual commands, executed in the player thread */
2016-05-15 00:25:56 -04:00
static enum command_state
get_status ( void * arg , int * retval )
2010-04-04 06:43:15 -04:00
{
2016-05-15 00:25:56 -04:00
union player_arg * cmdarg = arg ;
2010-04-04 06:43:15 -04:00
struct timespec ts ;
struct player_source * ps ;
struct player_status * status ;
uint64_t pos ;
int ret ;
2016-05-15 00:25:56 -04:00
status = cmdarg - > status ;
2010-04-04 06:43:15 -04:00
2014-12-21 14:41:44 -05:00
memset ( status , 0 , sizeof ( struct player_status ) ) ;
2010-04-04 06:43:15 -04:00
status - > shuffle = shuffle ;
status - > repeat = repeat ;
2010-11-19 14:35:12 -05:00
status - > volume = master_volume ;
2010-04-04 06:43:15 -04:00
2010-07-31 06:30:51 -04:00
status - > plid = cur_plid ;
2010-04-04 06:43:15 -04:00
switch ( player_state )
{
case PLAY_STOPPED :
2015-02-14 16:36:52 -05:00
DPRINTF ( E_DBG , L_PLAYER , " Player status: stopped \n " ) ;
2010-04-04 06:43:15 -04:00
status - > status = PLAY_STOPPED ;
break ;
case PLAY_PAUSED :
2015-02-14 16:36:52 -05:00
DPRINTF ( E_DBG , L_PLAYER , " Player status: paused \n " ) ;
2010-04-04 06:43:15 -04:00
status - > status = PLAY_PAUSED ;
status - > id = cur_streaming - > id ;
2015-10-03 03:01:26 -04:00
status - > item_id = cur_streaming - > item_id ;
2010-04-04 06:43:15 -04:00
pos = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES - cur_streaming - > stream_start ;
status - > pos_ms = ( pos * 1000 ) / 44100 ;
2015-02-14 16:36:52 -05:00
status - > len_ms = cur_streaming - > len_ms ;
2014-12-21 14:41:44 -05:00
2010-04-04 06:43:15 -04:00
break ;
case PLAY_PLAYING :
if ( ! cur_playing )
{
2015-02-14 16:36:52 -05:00
DPRINTF ( E_DBG , L_PLAYER , " Player status: playing (buffering) \n " ) ;
2010-04-04 06:43:15 -04:00
status - > status = PLAY_PAUSED ;
ps = cur_streaming ;
/* Avoid a visible 2-second jump backward for the client */
pos = ps - > output_start - ps - > stream_start ;
}
else
{
2015-02-14 16:36:52 -05:00
DPRINTF ( E_DBG , L_PLAYER , " Player status: playing \n " ) ;
2010-04-04 06:43:15 -04:00
status - > status = PLAY_PLAYING ;
ps = cur_playing ;
ret = player_get_current_pos ( & pos , & ts , 0 ) ;
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Could not get current stream position for playstatus \n " ) ;
pos = 0 ;
}
if ( pos < ps - > stream_start )
pos = 0 ;
else
pos - = ps - > stream_start ;
}
status - > pos_ms = ( pos * 1000 ) / 44100 ;
2015-02-14 16:36:52 -05:00
status - > len_ms = ps - > len_ms ;
2010-04-04 06:43:15 -04:00
status - > id = ps - > id ;
2015-10-03 03:01:26 -04:00
status - > item_id = ps - > item_id ;
2014-12-21 14:41:44 -05:00
2010-04-04 06:43:15 -04:00
break ;
}
2016-05-15 00:25:56 -04:00
* retval = 0 ;
return COMMAND_END ;
2010-04-04 06:43:15 -04:00
}
2016-05-15 00:25:56 -04:00
static enum command_state
now_playing ( void * arg , int * retval )
2010-04-04 06:43:15 -04:00
{
2016-05-15 00:25:56 -04:00
union player_arg * cmdarg = arg ;
2010-04-04 06:43:15 -04:00
uint32_t * id ;
2015-08-08 12:02:49 -04:00
struct player_source * ps_playing ;
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
id = cmdarg - > id_ptr ;
2010-04-04 06:43:15 -04:00
2015-08-08 12:02:49 -04:00
ps_playing = source_now_playing ( ) ;
if ( ps_playing )
* id = ps_playing - > id ;
2010-04-04 06:43:15 -04:00
else
2016-05-15 00:25:56 -04:00
{
* retval = - 1 ;
return COMMAND_END ;
}
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
* retval = 0 ;
return COMMAND_END ;
2010-04-04 06:43:15 -04:00
}
2016-05-15 00:25:56 -04:00
static enum command_state
artwork_url_get ( void * arg , int * retval )
2015-03-14 16:42:53 -04:00
{
2016-05-15 00:25:56 -04:00
union player_arg * cmdarg = arg ;
2015-04-09 16:33:23 -04:00
struct player_source * ps ;
2016-05-15 00:25:56 -04:00
cmdarg - > icy . artwork_url = NULL ;
2015-03-14 16:42:53 -04:00
2015-04-09 16:33:23 -04:00
if ( cur_playing )
ps = cur_playing ;
else if ( cur_streaming )
ps = cur_streaming ;
else
2016-05-15 00:25:56 -04:00
{
* retval = - 1 ;
return COMMAND_END ;
}
2015-04-09 16:33:23 -04:00
2015-04-09 16:22:42 -04:00
/* Check that we are playing a viable stream, and that it has the requested id */
2016-05-15 00:25:56 -04:00
if ( ! ps - > xcode | | ps - > data_kind ! = DATA_KIND_HTTP | | ps - > id ! = cmdarg - > icy . id )
{
* retval = - 1 ;
return COMMAND_END ;
}
2015-03-14 16:42:53 -04:00
2016-05-15 00:25:56 -04:00
cmdarg - > icy . artwork_url = transcode_metadata_artwork_url ( ps - > xcode ) ;
2015-03-14 16:42:53 -04:00
2016-05-15 00:25:56 -04:00
* retval = 0 ;
return COMMAND_END ;
2015-03-14 16:42:53 -04:00
}
2016-05-15 00:25:56 -04:00
static enum command_state
playback_stop ( void * arg , int * retval )
2010-04-04 06:43:15 -04:00
{
2015-08-08 12:02:49 -04:00
struct player_source * ps_playing ;
2010-12-12 04:25:21 -05:00
/* We may be restarting very soon, so we don't bring the devices to a
* full stop just yet ; this saves time when restarting , which is nicer
* for the user .
*/
2016-05-15 00:25:56 -04:00
* retval = outputs_flush ( device_command_cb , last_rtptime + AIRTUNES_V2_PACKET_SAMPLES ) ;
2010-12-12 04:25:21 -05:00
2014-09-27 16:59:19 -04:00
pb_timer_stop ( ) ;
2010-12-12 04:25:21 -05:00
2015-08-08 12:02:49 -04:00
ps_playing = source_now_playing ( ) ;
if ( ps_playing )
2014-05-03 13:44:26 -04:00
{
2015-10-03 03:01:26 -04:00
history_add ( ps_playing - > id , ps_playing - > item_id ) ;
2014-05-03 13:44:26 -04:00
}
2015-08-08 12:02:49 -04:00
2015-09-02 12:31:43 -04:00
source_stop ( ) ;
2010-12-12 04:25:21 -05:00
2014-09-28 16:09:22 -04:00
evbuffer_drain ( audio_buf , evbuffer_get_length ( audio_buf ) ) ;
2010-12-12 04:25:21 -05:00
status_update ( PLAY_STOPPED ) ;
2011-03-26 13:33:19 -04:00
metadata_purge ( ) ;
2016-04-04 10:58:07 -04:00
/* We're async if we need to flush devices */
2016-05-15 00:25:56 -04:00
if ( * retval > 0 )
return COMMAND_PENDING ; /* async */
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
return COMMAND_END ;
2010-04-04 06:43:15 -04:00
}
/* Playback startup bottom half */
2016-05-15 00:25:56 -04:00
static enum command_state
playback_start_bh ( void * arg , int * retval )
2010-04-04 06:43:15 -04:00
{
int ret ;
2016-04-02 08:05:17 -04:00
if ( output_sessions = = 0 )
2010-04-04 06:43:15 -04:00
{
DPRINTF ( E_LOG , L_PLAYER , " Cannot start playback: no output started \n " ) ;
goto out_fail ;
}
2011-02-17 10:21:35 -05:00
ret = clock_gettime_with_res ( CLOCK_MONOTONIC , & pb_pos_stamp , & timer_res ) ;
2010-04-04 06:43:15 -04:00
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Couldn't get current clock: %s \n " , strerror ( errno ) ) ;
goto out_fail ;
}
2014-09-27 16:59:19 -04:00
pb_timer_stop ( ) ;
2010-04-04 06:43:15 -04:00
2011-02-17 10:21:35 -05:00
/*
* initialize the packet timer to the same relative time that we have
* for the playback timer .
*/
packet_timer_last . tv_sec = pb_pos_stamp . tv_sec ;
packet_timer_last . tv_nsec = pb_pos_stamp . tv_nsec ;
2010-05-01 13:24:06 -04:00
pb_timer_last . tv_sec = pb_pos_stamp . tv_sec ;
pb_timer_last . tv_nsec = pb_pos_stamp . tv_nsec ;
2016-02-11 02:29:29 -05:00
ret = pb_timer_start ( ) ;
2010-04-04 06:43:15 -04:00
if ( ret < 0 )
2014-09-27 16:59:19 -04:00
goto out_fail ;
2010-04-04 06:43:15 -04:00
2016-01-23 19:14:07 -05:00
/* Everything OK, start outputs */
2016-03-22 17:59:50 -04:00
outputs_playback_start ( last_rtptime + AIRTUNES_V2_PACKET_SAMPLES , & pb_pos_stamp ) ;
2010-04-04 06:43:15 -04:00
status_update ( PLAY_PLAYING ) ;
2016-05-15 00:25:56 -04:00
* retval = 0 ;
return COMMAND_END ;
2010-04-04 06:43:15 -04:00
out_fail :
2010-12-12 04:12:39 -05:00
playback_abort ( ) ;
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
* retval = - 1 ;
return COMMAND_END ;
2010-04-04 06:43:15 -04:00
}
2016-05-15 00:25:56 -04:00
static enum command_state
2016-11-08 15:27:38 -05:00
playback_start_item ( void * arg , int * retval )
2010-04-04 06:43:15 -04:00
{
2016-11-08 15:27:38 -05:00
struct db_queue_item * queue_item = arg ;
2016-01-23 19:14:07 -05:00
struct output_device * device ;
2016-11-08 15:27:38 -05:00
struct player_source * ps ;
int seek_ms ;
2010-04-04 06:43:15 -04:00
int ret ;
if ( player_state = = PLAY_PLAYING )
{
status_update ( player_state ) ;
2016-10-23 02:18:38 -04:00
* retval = 1 ; // Value greater 0 will prevent execution of the bottom half function
2016-05-15 00:25:56 -04:00
return COMMAND_END ;
2010-04-04 06:43:15 -04:00
}
2014-12-21 14:41:44 -05:00
// Update global playback position
2010-04-04 06:43:15 -04:00
pb_pos = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES - 88200 ;
2016-11-08 15:27:38 -05:00
if ( player_state = = PLAY_STOPPED & & ! queue_item )
2010-04-04 06:43:15 -04:00
{
2016-11-08 15:27:38 -05:00
* retval = - 1 ;
return COMMAND_END ;
2010-04-04 06:43:15 -04:00
}
2016-11-08 15:27:38 -05:00
if ( ! queue_item )
2010-04-04 06:43:15 -04:00
{
2016-11-08 15:27:38 -05:00
// Resume playback of current source
ps = source_now_playing ( ) ;
DPRINTF ( E_DBG , L_PLAYER , " Resume playback of '%s' (id=%d, item-id=%d) \n " , ps - > path , ps - > id , ps - > item_id ) ;
2015-09-02 12:31:43 -04:00
}
2016-11-08 15:27:38 -05:00
else
2015-09-02 12:31:43 -04:00
{
2016-11-08 15:27:38 -05:00
// Start playback for given queue item
DPRINTF ( E_DBG , L_PLAYER , " Start playback of '%s' (id=%d, item-id=%d) \n " , queue_item - > path , queue_item - > file_id , queue_item - > item_id ) ;
2015-09-02 12:31:43 -04:00
source_stop ( ) ;
2016-11-08 15:27:38 -05:00
ps = source_new ( queue_item ) ;
if ( ! ps )
{
playback_abort ( ) ;
* retval = - 1 ;
return COMMAND_END ;
}
if ( queue_item - > file_id > 0 )
seek_ms = db_file_get_seekpos ( queue_item - > file_id ) ;
else
seek_ms = 0 ;
ret = source_open ( ps , last_rtptime + AIRTUNES_V2_PACKET_SAMPLES , seek_ms ) ;
2015-09-20 01:11:39 -04:00
if ( ret < 0 )
{
playback_abort ( ) ;
2016-08-06 00:29:55 -04:00
* retval = - 1 ;
return COMMAND_END ;
2015-09-20 01:11:39 -04:00
}
2010-04-04 06:43:15 -04:00
}
2015-09-02 12:31:43 -04:00
ret = source_play ( ) ;
if ( ret < 0 )
2011-03-26 13:33:19 -04:00
{
2015-09-02 12:31:43 -04:00
playback_abort ( ) ;
2016-05-15 00:25:56 -04:00
* retval = - 1 ;
return COMMAND_END ;
2011-03-26 13:33:19 -04:00
}
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
2015-09-26 02:45:52 -04:00
metadata_trigger ( 1 ) ;
2015-09-02 12:31:43 -04:00
2016-04-02 08:05:17 -04:00
/* Start sessions on selected devices */
2016-05-15 00:25:56 -04:00
* retval = 0 ;
2010-04-04 06:43:15 -04:00
2016-01-23 19:14:07 -05:00
for ( device = dev_list ; device ; device = device - > next )
2010-04-04 06:43:15 -04:00
{
2016-01-23 19:14:07 -05:00
if ( device - > selected & & ! device - > session )
2010-04-04 06:43:15 -04:00
{
2016-01-23 19:14:07 -05:00
ret = outputs_device_start ( device , device_restart_cb , last_rtptime + AIRTUNES_V2_PACKET_SAMPLES ) ;
2010-04-04 06:43:15 -04:00
if ( ret < 0 )
{
2016-01-23 19:14:07 -05:00
DPRINTF ( E_LOG , L_PLAYER , " Could not start selected %s device '%s' \n " , device - > type_name , device - > name ) ;
2010-04-04 06:43:15 -04:00
continue ;
}
2016-02-08 16:17:10 -05:00
DPRINTF ( E_INFO , L_PLAYER , " Using selected %s device '%s' \n " , device - > type_name , device - > name ) ;
2016-05-15 00:25:56 -04:00
( * retval ) + + ;
2010-04-04 06:43:15 -04:00
}
}
2016-09-10 17:26:06 -04:00
/* If autoselecting is enabled, try to autoselect a non-selected device if the above failed */
if ( speaker_autoselect & & ( * retval = = 0 ) & & ( output_sessions = = 0 ) )
2016-01-23 19:14:07 -05:00
for ( device = dev_list ; device ; device = device - > next )
2014-01-13 17:24:45 -05:00
{
2016-04-04 15:54:37 -04:00
if ( ( outputs_priority ( device ) = = 0 ) | | device - > session )
continue ;
2014-01-13 17:24:45 -05:00
2016-04-04 15:54:37 -04:00
speaker_select_output ( device ) ;
ret = outputs_device_start ( device , device_restart_cb , last_rtptime + AIRTUNES_V2_PACKET_SAMPLES ) ;
if ( ret < 0 )
{
DPRINTF ( E_DBG , L_PLAYER , " Could not autoselect %s device '%s' \n " , device - > type_name , device - > name ) ;
speaker_deselect_output ( device ) ;
continue ;
2014-01-13 17:24:45 -05:00
}
2016-04-04 15:54:37 -04:00
DPRINTF ( E_INFO , L_PLAYER , " Autoselecting %s device '%s' \n " , device - > type_name , device - > name ) ;
2016-05-15 00:25:56 -04:00
( * retval ) + + ;
2016-04-04 15:54:37 -04:00
break ;
2014-01-13 17:24:45 -05:00
}
/* No luck finding valid output */
2016-05-15 00:25:56 -04:00
if ( ( * retval = = 0 ) & & ( output_sessions = = 0 ) )
2010-04-04 06:43:15 -04:00
{
DPRINTF ( E_LOG , L_PLAYER , " Could not start playback: no output selected or couldn't start any output \n " ) ;
2010-12-12 04:12:39 -05:00
playback_abort ( ) ;
2016-05-15 00:25:56 -04:00
* retval = - 1 ;
return COMMAND_END ;
2010-04-04 06:43:15 -04:00
}
2016-04-04 10:58:07 -04:00
/* We're async if we need to start devices */
2016-05-15 00:25:56 -04:00
if ( * retval > 0 )
return COMMAND_PENDING ; /* async */
2010-04-04 06:43:15 -04:00
/* Otherwise, just run the bottom half */
2016-05-15 00:25:56 -04:00
* retval = 0 ;
return COMMAND_END ;
2010-04-04 06:43:15 -04:00
}
2016-05-15 00:25:56 -04:00
static enum command_state
playback_start ( void * arg , int * retval )
2015-08-08 12:02:49 -04:00
{
2016-11-08 15:27:38 -05:00
struct db_queue_item * queue_item = NULL ;
enum command_state cmd_state ;
2015-08-08 12:02:49 -04:00
2016-11-08 15:27:38 -05:00
if ( player_state = = PLAY_STOPPED )
2015-08-08 12:02:49 -04:00
{
2016-11-08 15:27:38 -05:00
// Start playback of first item in queue
queue_item = db_queue_fetch_bypos ( 0 , shuffle ) ;
if ( ! queue_item )
2015-08-08 12:02:49 -04:00
{
2016-11-08 15:27:38 -05:00
* retval = - 1 ;
return COMMAND_END ;
2015-08-08 12:02:49 -04:00
}
}
2016-11-08 15:27:38 -05:00
cmd_state = playback_start_item ( queue_item , retval ) ;
return cmd_state ;
2015-08-08 12:02:49 -04:00
}
2016-05-15 00:25:56 -04:00
static enum command_state
playback_prev_bh ( void * arg , int * retval )
2010-04-04 06:43:15 -04:00
{
int ret ;
2014-05-17 09:33:35 -04:00
int pos_sec ;
2016-11-08 15:27:38 -05:00
struct player_source * ps ;
2010-04-04 06:43:15 -04:00
2015-09-20 01:11:39 -04:00
/*
* The upper half is playback_pause , therefor the current playing item is
* already set as the cur_streaming ( cur_playing is NULL ) .
*/
2014-05-03 23:45:53 -04:00
if ( ! cur_streaming )
2014-05-03 13:44:26 -04:00
{
2014-05-03 23:45:53 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Could not get current stream source \n " ) ;
2016-05-15 00:25:56 -04:00
* retval = - 1 ;
return COMMAND_END ;
2014-05-03 13:44:26 -04:00
}
2010-04-04 06:43:15 -04:00
2014-05-03 23:45:53 -04:00
/* Only add to history if playback started. */
2015-09-02 12:31:43 -04:00
if ( cur_streaming - > output_start > cur_streaming - > stream_start )
2015-10-03 03:01:26 -04:00
history_add ( cur_streaming - > id , cur_streaming - > item_id ) ;
2014-05-03 23:45:53 -04:00
2014-05-17 09:33:35 -04:00
/* Compute the playing time in seconds for the current song. */
2015-09-02 12:31:43 -04:00
if ( cur_streaming - > output_start > cur_streaming - > stream_start )
pos_sec = ( cur_streaming - > output_start - cur_streaming - > stream_start ) / 44100 ;
2014-05-17 09:33:35 -04:00
else
pos_sec = 0 ;
/* Only skip to the previous song if the playing time is less than 3 seconds,
otherwise restart the current song . */
DPRINTF ( E_DBG , L_PLAYER , " Skipping song played %d sec \n " , pos_sec ) ;
if ( pos_sec < 3 )
2010-04-04 06:43:15 -04:00
{
2016-11-08 15:27:38 -05:00
ps = source_prev ( ) ;
if ( ! ps )
2015-09-02 12:31:43 -04:00
{
playback_abort ( ) ;
2016-05-15 00:25:56 -04:00
* retval = - 1 ;
return COMMAND_END ;
2015-09-02 12:31:43 -04:00
}
source_stop ( ) ;
2016-11-08 15:27:38 -05:00
ret = source_open ( ps , last_rtptime + AIRTUNES_V2_PACKET_SAMPLES , 0 ) ;
2014-05-17 09:33:35 -04:00
if ( ret < 0 )
{
playback_abort ( ) ;
2010-04-04 06:43:15 -04:00
2016-08-06 00:31:09 -04:00
* retval = - 1 ;
return COMMAND_END ;
2014-05-17 09:33:35 -04:00
}
}
else
{
2015-09-02 12:31:43 -04:00
ret = source_seek ( 0 ) ;
2014-05-17 09:33:35 -04:00
if ( ret < 0 )
{
playback_abort ( ) ;
2016-05-15 00:25:56 -04:00
* retval = - 1 ;
return COMMAND_END ;
2014-05-17 09:33:35 -04:00
}
2010-04-04 06:43:15 -04:00
}
if ( player_state = = PLAY_STOPPED )
2016-05-15 00:25:56 -04:00
{
* retval = - 1 ;
return COMMAND_END ;
}
2010-04-04 06:43:15 -04:00
/* Silent status change - playback_start() sends the real status update */
player_state = PLAY_PAUSED ;
2016-05-15 00:25:56 -04:00
* retval = 0 ;
return COMMAND_END ;
2010-04-04 06:43:15 -04:00
}
2015-09-20 01:11:39 -04:00
/*
* The bottom half of the next command
*/
2016-05-15 00:25:56 -04:00
static enum command_state
playback_next_bh ( void * arg , int * retval )
2010-04-04 06:43:15 -04:00
{
2016-11-08 15:27:38 -05:00
struct player_source * ps ;
2010-04-04 06:43:15 -04:00
int ret ;
2015-09-20 01:11:39 -04:00
/*
* The upper half is playback_pause , therefor the current playing item is
* already set as the cur_streaming ( cur_playing is NULL ) .
*/
2014-05-03 23:45:53 -04:00
if ( ! cur_streaming )
2014-05-03 13:44:26 -04:00
{
2014-05-03 23:45:53 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Could not get current stream source \n " ) ;
2016-05-15 00:25:56 -04:00
* retval = - 1 ;
return COMMAND_END ;
2014-05-03 13:44:26 -04:00
}
2010-04-04 06:43:15 -04:00
2014-05-03 23:45:53 -04:00
/* Only add to history if playback started. */
2015-09-02 12:31:43 -04:00
if ( cur_streaming - > output_start > cur_streaming - > stream_start )
2015-10-03 03:01:26 -04:00
history_add ( cur_streaming - > id , cur_streaming - > item_id ) ;
2014-05-03 23:45:53 -04:00
2016-11-08 15:27:38 -05:00
ps = source_next ( ) ;
if ( ! ps )
2015-09-02 12:31:43 -04:00
{
playback_abort ( ) ;
2016-05-15 00:25:56 -04:00
* retval = - 1 ;
return COMMAND_END ;
2015-09-02 12:31:43 -04:00
}
source_stop ( ) ;
2014-05-03 23:45:53 -04:00
2016-11-08 15:27:38 -05:00
ret = source_open ( ps , last_rtptime + AIRTUNES_V2_PACKET_SAMPLES , 0 ) ;
2010-04-04 06:43:15 -04:00
if ( ret < 0 )
{
2010-12-12 04:12:39 -05:00
playback_abort ( ) ;
2016-05-15 00:25:56 -04:00
* retval = - 1 ;
return COMMAND_END ;
2010-04-04 06:43:15 -04:00
}
if ( player_state = = PLAY_STOPPED )
2016-05-15 00:25:56 -04:00
{
* retval = - 1 ;
return COMMAND_END ;
}
2010-04-04 06:43:15 -04:00
/* Silent status change - playback_start() sends the real status update */
player_state = PLAY_PAUSED ;
2016-05-15 00:25:56 -04:00
* retval = 0 ;
return COMMAND_END ;
2010-04-04 06:43:15 -04:00
}
2016-05-15 00:25:56 -04:00
static enum command_state
playback_seek_bh ( void * arg , int * retval )
2010-04-04 06:43:15 -04:00
{
2016-05-15 00:25:56 -04:00
union player_arg * cmdarg = arg ;
2010-04-04 06:43:15 -04:00
int ms ;
int ret ;
2016-05-15 00:25:56 -04:00
ms = cmdarg - > intval ;
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
ret = source_seek ( ms ) ;
2014-03-11 18:20:29 -04:00
2010-04-04 06:43:15 -04:00
if ( ret < 0 )
{
2010-12-12 04:12:39 -05:00
playback_abort ( ) ;
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
* retval = - 1 ;
return COMMAND_END ;
2010-04-04 06:43:15 -04:00
}
/* Silent status change - playback_start() sends the real status update */
player_state = PLAY_PAUSED ;
2016-05-15 00:25:56 -04:00
* retval = 0 ;
return COMMAND_END ;
2010-04-04 06:43:15 -04:00
}
2016-05-15 00:25:56 -04:00
static enum command_state
playback_pause_bh ( void * arg , int * retval )
2010-04-04 06:43:15 -04:00
{
int ret ;
2015-10-25 03:58:47 -04:00
if ( cur_streaming - > data_kind = = DATA_KIND_HTTP
| | cur_streaming - > data_kind = = DATA_KIND_PIPE )
{
DPRINTF ( E_DBG , L_PLAYER , " Source is not pausable, abort playback \n " ) ;
playback_abort ( ) ;
2016-05-15 00:25:56 -04:00
* retval = - 1 ;
return COMMAND_END ;
2015-10-25 03:58:47 -04:00
}
2015-09-02 12:31:43 -04:00
status_update ( PLAY_PAUSED ) ;
2014-03-11 18:20:29 -04:00
2015-09-02 12:31:43 -04:00
if ( cur_streaming - > media_kind & ( MEDIA_KIND_MOVIE | MEDIA_KIND_PODCAST | MEDIA_KIND_AUDIOBOOK | MEDIA_KIND_TVSHOW ) )
2010-04-04 06:43:15 -04:00
{
2015-09-02 12:31:43 -04:00
ret = ( cur_streaming - > output_start - cur_streaming - > stream_start ) / 44100 * 1000 ;
db_file_save_seek ( cur_streaming - > id , ret ) ;
2010-04-04 06:43:15 -04:00
}
2016-05-15 00:25:56 -04:00
* retval = 0 ;
return COMMAND_END ;
2010-04-04 06:43:15 -04:00
}
2016-05-15 00:25:56 -04:00
static enum command_state
playback_pause ( void * arg , int * retval )
2010-04-04 06:43:15 -04:00
{
uint64_t pos ;
pos = source_check ( ) ;
if ( pos = = 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Could not retrieve current position for pause \n " ) ;
2010-12-12 04:12:39 -05:00
playback_abort ( ) ;
2016-10-23 02:18:38 -04:00
* retval = - 1 ;
return COMMAND_END ;
2010-04-04 06:43:15 -04:00
}
/* Make sure playback is still running after source_check() */
if ( player_state = = PLAY_STOPPED )
2016-05-15 00:25:56 -04:00
{
* retval = - 1 ;
return COMMAND_END ;
}
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
* retval = outputs_flush ( device_command_cb , last_rtptime + AIRTUNES_V2_PACKET_SAMPLES ) ;
2010-04-04 06:43:15 -04:00
2014-09-27 16:59:19 -04:00
pb_timer_stop ( ) ;
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
source_pause ( pos ) ;
2010-04-04 06:43:15 -04:00
2014-09-28 16:09:22 -04:00
evbuffer_drain ( audio_buf , evbuffer_get_length ( audio_buf ) ) ;
2010-04-04 06:43:15 -04:00
2011-03-26 13:33:19 -04:00
metadata_purge ( ) ;
2016-04-04 10:58:07 -04:00
/* We're async if we need to flush devices */
2016-05-15 00:25:56 -04:00
if ( * retval > 0 )
return COMMAND_PENDING ; /* async */
2010-04-04 06:43:15 -04:00
/* Otherwise, just run the bottom half */
2016-05-15 00:25:56 -04:00
return COMMAND_END ;
2010-04-04 06:43:15 -04:00
}
2016-05-15 00:25:56 -04:00
static enum command_state
speaker_enumerate ( void * arg , int * retval )
2010-12-02 13:51:08 -05:00
{
2016-05-15 00:25:56 -04:00
union player_arg * cmdarg = arg ;
2016-01-23 19:14:07 -05:00
struct output_device * device ;
2010-12-02 13:51:08 -05:00
struct spk_enum * spk_enum ;
2011-03-05 04:25:44 -05:00
struct spk_flags flags ;
2010-12-02 13:51:08 -05:00
2016-05-15 00:25:56 -04:00
spk_enum = cmdarg - > spk_enum ;
2010-12-02 13:51:08 -05:00
2010-11-19 16:51:46 -05:00
# ifdef DEBUG_RELVOL
DPRINTF ( E_DBG , L_PLAYER , " *** master: %d \n " , master_volume ) ;
# endif
2010-12-02 13:51:08 -05:00
2016-01-23 19:14:07 -05:00
for ( device = dev_list ; device ; device = device - > next )
2010-12-02 13:51:08 -05:00
{
2016-01-23 19:14:07 -05:00
if ( device - > advertised | | device - > selected )
2010-11-19 16:51:46 -05:00
{
2016-01-23 19:14:07 -05:00
flags . selected = device - > selected ;
flags . has_password = device - > has_password ;
flags . has_video = device - > has_video ;
2011-03-05 04:25:44 -05:00
2016-10-23 03:42:20 -04:00
spk_enum - > cb ( device - > id , device - > name , device - > relvol , device - > volume , flags , spk_enum - > arg ) ;
2010-11-19 16:51:46 -05:00
# ifdef DEBUG_RELVOL
2016-01-23 19:14:07 -05:00
DPRINTF ( E_DBG , L_PLAYER , " *** %s: abs %d rel %d \n " , device - > name , device - > volume , device - > relvol ) ;
2010-11-19 16:51:46 -05:00
# endif
}
2010-12-02 13:51:08 -05:00
}
2016-05-15 00:25:56 -04:00
* retval = 0 ;
return COMMAND_END ;
2010-12-02 13:51:08 -05:00
}
2010-04-04 06:43:15 -04:00
static int
2016-01-23 19:14:07 -05:00
speaker_activate ( struct output_device * device )
2010-04-04 06:43:15 -04:00
{
int ret ;
2016-01-23 19:14:07 -05:00
if ( ! device )
2010-04-04 06:43:15 -04:00
{
2016-04-02 08:05:17 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Bug! speaker_activate called with device \n " ) ;
return - 1 ;
}
2010-04-04 06:43:15 -04:00
2016-04-02 08:05:17 -04:00
if ( player_state = = PLAY_PLAYING )
{
DPRINTF ( E_DBG , L_PLAYER , " Activating %s device '%s' \n " , device - > type_name , device - > name ) ;
2010-04-04 06:43:15 -04:00
2016-04-02 08:05:17 -04:00
ret = outputs_device_start ( device , device_activate_cb , last_rtptime + AIRTUNES_V2_PACKET_SAMPLES ) ;
if ( ret < 0 )
2010-04-04 06:43:15 -04:00
{
2016-04-02 08:05:17 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Could not start %s device '%s' \n " , device - > type_name , device - > name ) ;
return - 1 ;
2010-04-04 06:43:15 -04:00
}
}
else
{
2016-04-02 08:05:17 -04:00
DPRINTF ( E_DBG , L_PLAYER , " Probing %s device '%s' \n " , device - > type_name , device - > name ) ;
2010-04-04 06:43:15 -04:00
2016-04-02 08:05:17 -04:00
ret = outputs_device_probe ( device , device_probe_cb ) ;
if ( ret < 0 )
2010-04-04 06:43:15 -04:00
{
2016-04-02 08:05:17 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Could not probe %s device '%s' \n " , device - > type_name , device - > name ) ;
return - 1 ;
2010-04-04 06:43:15 -04:00
}
}
2016-04-02 08:05:17 -04:00
return 0 ;
2010-04-04 06:43:15 -04:00
}
static int
2016-01-23 19:14:07 -05:00
speaker_deactivate ( struct output_device * device )
2010-04-04 06:43:15 -04:00
{
2016-04-02 08:05:17 -04:00
DPRINTF ( E_DBG , L_PLAYER , " Deactivating %s device '%s' \n " , device - > type_name , device - > name ) ;
2010-04-04 06:43:15 -04:00
2016-04-02 08:05:17 -04:00
outputs_status_cb ( device - > session , device_shutdown_cb ) ;
outputs_device_stop ( device - > session ) ;
2010-04-04 06:43:15 -04:00
2016-04-02 08:05:17 -04:00
return 0 ;
2010-04-04 06:43:15 -04:00
}
2016-05-15 00:25:56 -04:00
static enum command_state
speaker_set ( void * arg , int * retval )
2010-04-04 06:43:15 -04:00
{
2016-05-15 00:25:56 -04:00
union player_arg * cmdarg = arg ;
2016-01-23 19:14:07 -05:00
struct output_device * device ;
2010-04-04 06:43:15 -04:00
uint64_t * ids ;
int nspk ;
int i ;
int ret ;
2016-05-15 00:25:56 -04:00
* retval = 0 ;
ids = cmdarg - > speaker_set_param . device_ids ;
2010-04-04 06:43:15 -04:00
if ( ids )
nspk = ids [ 0 ] ;
else
nspk = 0 ;
DPRINTF ( E_DBG , L_PLAYER , " Speaker set: %d speakers \n " , nspk ) ;
2016-05-15 00:25:56 -04:00
* retval = 0 ;
2010-04-04 06:43:15 -04:00
2016-01-23 19:14:07 -05:00
for ( device = dev_list ; device ; device = device - > next )
2010-04-04 06:43:15 -04:00
{
for ( i = 1 ; i < = nspk ; i + + )
{
2016-01-23 19:14:07 -05:00
DPRINTF ( E_DBG , L_PLAYER , " Set % " PRIu64 " device % " PRIu64 " \n " , ids [ i ] , device - > id ) ;
2010-04-04 06:43:15 -04:00
2016-01-23 19:14:07 -05:00
if ( ids [ i ] = = device - > id )
2010-04-04 06:43:15 -04:00
break ;
}
if ( i < = nspk )
{
2016-01-23 19:14:07 -05:00
if ( device - > has_password & & ! device - > password )
2010-04-04 06:43:15 -04:00
{
2016-01-23 19:14:07 -05:00
DPRINTF ( E_INFO , L_PLAYER , " The %s device '%s' is password-protected, but we don't have it \n " , device - > type_name , device - > name ) ;
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
cmdarg - > speaker_set_param . intval = - 2 ;
2010-04-04 06:43:15 -04:00
continue ;
}
2016-01-23 19:14:07 -05:00
DPRINTF ( E_DBG , L_PLAYER , " The %s device '%s' is selected \n " , device - > type_name , device - > name ) ;
2010-11-21 04:05:41 -05:00
2016-01-23 19:14:07 -05:00
if ( ! device - > selected )
speaker_select_output ( device ) ;
2010-04-04 06:43:15 -04:00
2016-01-23 19:14:07 -05:00
if ( ! device - > session )
2010-04-04 06:43:15 -04:00
{
2016-01-23 19:14:07 -05:00
ret = speaker_activate ( device ) ;
2010-04-04 06:43:15 -04:00
if ( ret < 0 )
{
2016-01-23 19:14:07 -05:00
DPRINTF ( E_LOG , L_PLAYER , " Could not activate %s device '%s' \n " , device - > type_name , device - > name ) ;
2010-04-04 06:43:15 -04:00
2016-01-23 19:14:07 -05:00
speaker_deselect_output ( device ) ;
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
if ( cmdarg - > speaker_set_param . intval ! = - 2 )
cmdarg - > speaker_set_param . intval = - 1 ;
2010-04-04 06:43:15 -04:00
}
2016-04-04 10:58:07 -04:00
else
2016-05-15 00:25:56 -04:00
( * retval ) + + ;
2010-04-04 06:43:15 -04:00
}
}
else
{
2016-01-23 19:14:07 -05:00
DPRINTF ( E_DBG , L_PLAYER , " The %s device '%s' is NOT selected \n " , device - > type_name , device - > name ) ;
2010-11-21 04:05:41 -05:00
2016-01-23 19:14:07 -05:00
if ( device - > selected )
speaker_deselect_output ( device ) ;
2010-04-04 06:43:15 -04:00
2016-01-23 19:14:07 -05:00
if ( device - > session )
2010-04-04 06:43:15 -04:00
{
2016-01-23 19:14:07 -05:00
ret = speaker_deactivate ( device ) ;
2010-04-04 06:43:15 -04:00
if ( ret < 0 )
{
2016-01-23 19:14:07 -05:00
DPRINTF ( E_LOG , L_PLAYER , " Could not deactivate %s device '%s' \n " , device - > type_name , device - > name ) ;
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
if ( cmdarg - > speaker_set_param . intval ! = - 2 )
cmdarg - > speaker_set_param . intval = - 1 ;
2010-04-04 06:43:15 -04:00
}
2016-04-04 10:58:07 -04:00
else
2016-05-15 00:25:56 -04:00
( * retval ) + + ;
2010-04-04 06:43:15 -04:00
}
}
}
2015-05-03 02:45:38 -04:00
listener_notify ( LISTENER_SPEAKER ) ;
2016-05-15 00:25:56 -04:00
if ( * retval > 0 )
return COMMAND_PENDING ; /* async */
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
* retval = cmdarg - > speaker_set_param . intval ;
return COMMAND_END ;
2010-04-04 06:43:15 -04:00
}
2016-05-15 00:25:56 -04:00
static enum command_state
volume_set ( void * arg , int * retval )
2010-04-04 06:43:15 -04:00
{
2016-05-15 00:25:56 -04:00
union player_arg * cmdarg = arg ;
2016-01-23 19:14:07 -05:00
struct output_device * device ;
2010-11-19 16:51:46 -05:00
int volume ;
2010-11-19 15:22:55 -05:00
2016-05-15 00:25:56 -04:00
* retval = 0 ;
volume = cmdarg - > intval ;
2010-04-04 06:43:15 -04:00
2010-11-19 16:51:46 -05:00
if ( master_volume = = volume )
2016-05-15 00:25:56 -04:00
return COMMAND_END ;
2010-11-19 16:51:46 -05:00
master_volume = volume ;
2016-01-23 19:14:07 -05:00
for ( device = dev_list ; device ; device = device - > next )
2010-11-19 15:22:55 -05:00
{
2016-01-23 19:14:07 -05:00
if ( ! device - > selected )
2010-11-19 16:51:46 -05:00
continue ;
2016-01-23 19:14:07 -05:00
device - > volume = rel_to_vol ( device - > relvol ) ;
2010-11-19 16:51:46 -05:00
# ifdef DEBUG_RELVOL
2016-01-23 19:14:07 -05:00
DPRINTF ( E_DBG , L_PLAYER , " *** %s: abs %d rel %d \n " , device - > name , device - > volume , device - > relvol ) ;
2010-11-19 16:51:46 -05:00
# endif
2010-11-19 15:49:17 -05:00
2016-01-23 19:14:07 -05:00
if ( device - > session )
2016-05-15 00:25:56 -04:00
* retval + = outputs_device_volume_set ( device , device_command_cb ) ;
2010-11-19 15:22:55 -05:00
}
2010-04-04 06:43:15 -04:00
2015-05-03 02:45:38 -04:00
listener_notify ( LISTENER_VOLUME ) ;
2016-05-15 00:25:56 -04:00
if ( * retval > 0 )
return COMMAND_PENDING ; /* async */
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
return COMMAND_END ;
2010-04-04 06:43:15 -04:00
}
2016-05-15 00:25:56 -04:00
static enum command_state
volume_setrel_speaker ( void * arg , int * retval )
2010-11-19 16:51:46 -05:00
{
2016-05-15 00:25:56 -04:00
union player_arg * cmdarg = arg ;
2016-01-23 19:14:07 -05:00
struct output_device * device ;
2010-11-19 16:51:46 -05:00
uint64_t id ;
int relvol ;
2016-05-15 00:25:56 -04:00
* retval = 0 ;
id = cmdarg - > vol_param . spk_id ;
relvol = cmdarg - > vol_param . volume ;
2010-11-19 16:51:46 -05:00
2016-04-02 08:05:17 -04:00
for ( device = dev_list ; device ; device = device - > next )
2010-11-19 16:51:46 -05:00
{
2016-04-02 08:05:17 -04:00
if ( device - > id ! = id )
continue ;
2010-11-19 16:51:46 -05:00
2016-04-02 08:05:17 -04:00
if ( ! device - > selected )
2016-08-06 00:34:34 -04:00
{
* retval = 0 ;
return COMMAND_END ;
}
2010-11-19 16:51:46 -05:00
2016-04-02 08:05:17 -04:00
device - > relvol = relvol ;
device - > volume = rel_to_vol ( relvol ) ;
2010-11-19 16:51:46 -05:00
# ifdef DEBUG_RELVOL
2016-04-02 08:05:17 -04:00
DPRINTF ( E_DBG , L_PLAYER , " *** %s: abs %d rel %d \n " , device - > name , device - > volume , device - > relvol ) ;
2010-11-19 16:51:46 -05:00
# endif
2016-04-02 08:05:17 -04:00
if ( device - > session )
2016-05-15 00:25:56 -04:00
* retval = outputs_device_volume_set ( device , device_command_cb ) ;
2010-11-19 16:51:46 -05:00
2016-04-02 08:05:17 -04:00
break ;
2010-11-19 16:51:46 -05:00
}
2015-05-03 02:45:38 -04:00
listener_notify ( LISTENER_VOLUME ) ;
2016-05-15 00:25:56 -04:00
if ( * retval > 0 )
return COMMAND_PENDING ; /* async */
2010-11-19 16:51:46 -05:00
2016-05-15 00:25:56 -04:00
return COMMAND_END ;
2010-11-19 16:51:46 -05:00
}
2016-05-15 00:25:56 -04:00
static enum command_state
volume_setabs_speaker ( void * arg , int * retval )
2010-11-19 16:51:46 -05:00
{
2016-05-15 00:25:56 -04:00
union player_arg * cmdarg = arg ;
2016-01-23 19:14:07 -05:00
struct output_device * device ;
2010-11-19 16:51:46 -05:00
uint64_t id ;
int volume ;
2016-05-15 00:25:56 -04:00
* retval = 0 ;
id = cmdarg - > vol_param . spk_id ;
volume = cmdarg - > vol_param . volume ;
2010-11-19 16:51:46 -05:00
master_volume = volume ;
2016-01-23 19:14:07 -05:00
for ( device = dev_list ; device ; device = device - > next )
2010-11-19 16:51:46 -05:00
{
2016-01-23 19:14:07 -05:00
if ( ! device - > selected )
2010-11-19 16:51:46 -05:00
continue ;
2016-01-23 19:14:07 -05:00
if ( device - > id ! = id )
2010-11-19 16:51:46 -05:00
{
2016-01-23 19:14:07 -05:00
device - > relvol = vol_to_rel ( device - > volume ) ;
2010-11-19 16:51:46 -05:00
# ifdef DEBUG_RELVOL
2016-01-23 19:14:07 -05:00
DPRINTF ( E_DBG , L_PLAYER , " *** %s: abs %d rel %d \n " , device - > name , device - > volume , device - > relvol ) ;
2010-11-19 16:51:46 -05:00
# endif
continue ;
}
else
{
2016-01-23 19:14:07 -05:00
device - > relvol = 100 ;
device - > volume = master_volume ;
2010-11-19 16:51:46 -05:00
# ifdef DEBUG_RELVOL
2016-01-23 19:14:07 -05:00
DPRINTF ( E_DBG , L_PLAYER , " *** %s: abs %d rel %d \n " , device - > name , device - > volume , device - > relvol ) ;
2010-11-19 16:51:46 -05:00
# endif
2016-01-23 19:14:07 -05:00
if ( device - > session )
2016-05-15 00:25:56 -04:00
* retval = outputs_device_volume_set ( device , device_command_cb ) ; //FIXME Does this need to be += ?
2010-11-19 16:51:46 -05:00
}
}
2015-05-03 02:45:38 -04:00
listener_notify ( LISTENER_VOLUME ) ;
2016-05-15 00:25:56 -04:00
if ( * retval > 0 )
return COMMAND_PENDING ; /* async */
2010-11-19 16:51:46 -05:00
2016-05-15 00:25:56 -04:00
return COMMAND_END ;
2010-11-19 16:51:46 -05:00
}
2016-05-15 00:25:56 -04:00
static enum command_state
repeat_set ( void * arg , int * retval )
2010-04-04 06:43:15 -04:00
{
2016-05-15 00:25:56 -04:00
union player_arg * cmdarg = arg ;
if ( cmdarg - > mode = = repeat )
2016-08-06 00:36:42 -04:00
{
* retval = 0 ;
return COMMAND_END ;
}
2015-05-03 02:45:38 -04:00
2016-05-15 00:25:56 -04:00
switch ( cmdarg - > mode )
2010-04-04 06:43:15 -04:00
{
case REPEAT_OFF :
case REPEAT_SONG :
case REPEAT_ALL :
2016-05-15 00:25:56 -04:00
repeat = cmdarg - > mode ;
2010-04-04 06:43:15 -04:00
break ;
default :
2016-05-15 00:25:56 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Invalid repeat mode: %d \n " , cmdarg - > mode ) ;
* retval = - 1 ;
return COMMAND_END ;
2010-04-04 06:43:15 -04:00
}
2015-05-03 02:45:38 -04:00
listener_notify ( LISTENER_OPTIONS ) ;
2016-05-15 00:25:56 -04:00
* retval = 0 ;
return COMMAND_END ;
2010-04-04 06:43:15 -04:00
}
2016-05-15 00:25:56 -04:00
static enum command_state
shuffle_set ( void * arg , int * retval )
2010-04-04 06:43:15 -04:00
{
2016-05-15 00:25:56 -04:00
union player_arg * cmdarg = arg ;
2015-08-08 12:02:49 -04:00
uint32_t cur_id ;
2016-05-15 00:25:56 -04:00
switch ( cmdarg - > intval )
2010-04-04 06:43:15 -04:00
{
case 1 :
if ( ! shuffle )
2015-08-08 12:02:49 -04:00
{
2015-10-03 03:01:26 -04:00
cur_id = cur_streaming ? cur_streaming - > item_id : 0 ;
2016-11-08 15:27:38 -05:00
db_queue_reshuffle ( cur_id ) ;
2015-08-08 12:02:49 -04:00
}
2010-04-04 06:43:15 -04:00
/* FALLTHROUGH*/
case 0 :
2016-05-15 00:25:56 -04:00
shuffle = cmdarg - > intval ;
2010-04-04 06:43:15 -04:00
break ;
default :
2016-05-15 00:25:56 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Invalid shuffle mode: %d \n " , cmdarg - > intval ) ;
* retval = - 1 ;
return COMMAND_END ;
2010-04-04 06:43:15 -04:00
}
2015-05-03 02:45:38 -04:00
listener_notify ( LISTENER_OPTIONS ) ;
2016-05-15 00:25:56 -04:00
* retval = 0 ;
return COMMAND_END ;
2010-04-04 06:43:15 -04:00
}
2014-05-29 03:45:04 -04:00
/*
2015-08-08 12:02:49 -04:00
* Removes all items from the history
2014-05-29 03:45:04 -04:00
*/
2016-05-15 00:25:56 -04:00
static enum command_state
playerqueue_clear_history ( void * arg , int * retval )
2014-05-17 08:06:50 -04:00
{
2015-08-08 12:02:49 -04:00
memset ( history , 0 , sizeof ( struct player_history ) ) ;
2014-05-17 08:06:50 -04:00
2016-11-08 15:27:38 -05:00
cur_plversion + + ; // TODO [db_queue] need to update db queue version
2015-05-03 04:34:49 -04:00
2015-05-03 02:45:38 -04:00
listener_notify ( LISTENER_PLAYLIST ) ;
2016-05-15 00:25:56 -04:00
* retval = 0 ;
return COMMAND_END ;
2014-05-17 08:06:50 -04:00
}
2016-05-15 00:25:56 -04:00
static enum command_state
playerqueue_plid ( void * arg , int * retval )
2010-07-31 06:30:51 -04:00
{
2016-05-15 00:25:56 -04:00
union player_arg * cmdarg = arg ;
cur_plid = cmdarg - > id ;
2010-07-31 06:30:51 -04:00
2016-05-15 00:25:56 -04:00
* retval = 0 ;
return COMMAND_END ;
2010-04-04 06:43:15 -04:00
}
/* Player API executed in the httpd (DACP) thread */
int
player_get_status ( struct player_status * status )
{
2016-05-15 00:25:56 -04:00
union player_arg cmdarg ;
2010-04-04 06:43:15 -04:00
int ret ;
2016-05-15 00:25:56 -04:00
cmdarg . status = status ;
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
ret = commands_exec_sync ( cmdbase , get_status , NULL , & cmdarg ) ;
2010-04-04 06:43:15 -04:00
return ret ;
}
2015-08-08 12:02:49 -04:00
/*
* Stores the now playing media item dbmfi - id in the given id pointer .
*
* @ param id Pointer will hold the playing item ( dbmfi ) id if the function returns 0
* @ return 0 on success , - 1 on failure ( e . g . no playing item found )
*/
2010-04-04 06:43:15 -04:00
int
player_now_playing ( uint32_t * id )
{
2016-05-15 00:25:56 -04:00
union player_arg cmdarg ;
2010-04-04 06:43:15 -04:00
int ret ;
2016-05-15 00:25:56 -04:00
cmdarg . id_ptr = id ;
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
ret = commands_exec_sync ( cmdbase , now_playing , NULL , & cmdarg ) ;
2015-03-14 16:42:53 -04:00
return ret ;
}
2015-04-09 16:22:42 -04:00
char *
player_get_icy_artwork_url ( uint32_t id )
2015-03-14 16:42:53 -04:00
{
2016-05-15 00:25:56 -04:00
union player_arg cmdarg ;
2015-03-14 16:42:53 -04:00
int ret ;
2016-05-15 00:25:56 -04:00
cmdarg . icy . id = id ;
2015-03-14 16:42:53 -04:00
if ( pthread_self ( ) ! = tid_player )
2016-05-15 00:25:56 -04:00
ret = commands_exec_sync ( cmdbase , artwork_url_get , NULL , & cmdarg ) ;
2015-03-14 16:42:53 -04:00
else
2016-05-15 00:25:56 -04:00
artwork_url_get ( & cmdarg , & ret ) ;
2015-03-14 16:42:53 -04:00
2015-04-09 16:22:42 -04:00
if ( ret < 0 )
return NULL ;
else
2016-05-15 00:25:56 -04:00
return cmdarg . icy . artwork_url ;
2010-04-04 06:43:15 -04:00
}
2015-02-14 16:36:52 -05:00
/*
* Starts / resumes playback
*
2015-08-08 12:02:49 -04:00
* Depending on the player state , this will either resume playing the current item ( player is paused )
* or begin playing the queue from the beginning .
2015-02-14 16:36:52 -05:00
*
* If shuffle is set , the queue is reshuffled prior to starting playback .
*
2015-08-08 12:02:49 -04:00
* If a pointer is given as argument " itemid " , its value will be set to the playing item dbmfi - id .
2015-02-14 16:36:52 -05:00
*
2015-10-03 02:49:04 -04:00
* @ param * id if not NULL , will be set to the playing item dbmfi - id
2015-02-14 16:36:52 -05:00
* @ return 0 if successful , - 1 if an error occurred
*/
2010-04-04 06:43:15 -04:00
int
2016-11-13 06:24:25 -05:00
player_playback_start ( )
2014-12-21 14:41:44 -05:00
{
int ret ;
2016-11-13 06:24:25 -05:00
ret = commands_exec_sync ( cmdbase , playback_start , playback_start_bh , NULL ) ;
2014-12-21 14:41:44 -05:00
return ret ;
}
2015-02-14 16:36:52 -05:00
/*
2015-08-08 12:02:49 -04:00
* Starts playback with the media item at the given index of the play - queue .
2015-02-14 16:36:52 -05:00
*
* If shuffle is set , the queue is reshuffled prior to starting playback .
*
* If a pointer is given as argument " itemid " , its value will be set to the playing item id .
*
2015-08-08 12:02:49 -04:00
* @ param index the index of the item in the play - queue
2015-10-03 02:49:04 -04:00
* @ param * id if not NULL , will be set to the playing item id
2015-02-14 16:36:52 -05:00
* @ return 0 if successful , - 1 if an error occurred
*/
2014-12-21 14:41:44 -05:00
int
2016-11-08 15:27:38 -05:00
player_playback_start_byitem ( struct db_queue_item * queue_item )
2014-12-21 14:41:44 -05:00
{
int ret ;
2010-04-04 06:43:15 -04:00
2016-11-08 15:27:38 -05:00
ret = commands_exec_sync ( cmdbase , playback_start_item , playback_start_bh , queue_item ) ;
2010-04-04 06:43:15 -04:00
return ret ;
}
int
player_playback_stop ( void )
{
int ret ;
2016-05-15 00:25:56 -04:00
ret = commands_exec_sync ( cmdbase , playback_stop , NULL , NULL ) ;
2010-04-04 06:43:15 -04:00
return ret ;
}
int
player_playback_pause ( void )
{
int ret ;
2016-05-15 00:25:56 -04:00
ret = commands_exec_sync ( cmdbase , playback_pause , playback_pause_bh , NULL ) ;
2010-04-04 06:43:15 -04:00
return ret ;
}
int
player_playback_seek ( int ms )
{
2016-05-15 00:25:56 -04:00
union player_arg cmdarg ;
2010-04-04 06:43:15 -04:00
int ret ;
2016-05-15 00:25:56 -04:00
cmdarg . intval = ms ;
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
ret = commands_exec_sync ( cmdbase , playback_pause , playback_seek_bh , & cmdarg ) ;
2010-04-04 06:43:15 -04:00
return ret ;
}
int
player_playback_next ( void )
{
int ret ;
2016-05-15 00:25:56 -04:00
ret = commands_exec_sync ( cmdbase , playback_pause , playback_next_bh , NULL ) ;
2010-04-04 06:43:15 -04:00
return ret ;
}
int
player_playback_prev ( void )
{
int ret ;
2016-05-15 00:25:56 -04:00
ret = commands_exec_sync ( cmdbase , playback_pause , playback_prev_bh , NULL ) ;
2010-04-04 06:43:15 -04:00
return ret ;
}
2015-10-09 17:58:27 -04:00
2010-04-04 06:43:15 -04:00
void
player_speaker_enumerate ( spk_enum_cb cb , void * arg )
{
2016-05-15 00:25:56 -04:00
union player_arg cmdarg ;
2010-12-02 13:51:08 -05:00
struct spk_enum spk_enum ;
2010-04-04 06:43:15 -04:00
2010-12-02 13:51:08 -05:00
spk_enum . cb = cb ;
spk_enum . arg = arg ;
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
cmdarg . spk_enum = & spk_enum ;
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
commands_exec_sync ( cmdbase , speaker_enumerate , NULL , & cmdarg ) ;
2010-04-04 06:43:15 -04:00
}
int
player_speaker_set ( uint64_t * ids )
{
2016-05-15 00:25:56 -04:00
union player_arg cmdarg ;
2010-04-04 06:43:15 -04:00
int ret ;
2016-05-15 00:25:56 -04:00
cmdarg . speaker_set_param . device_ids = ids ;
cmdarg . speaker_set_param . intval = 0 ;
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
ret = commands_exec_sync ( cmdbase , speaker_set , NULL , & cmdarg ) ;
2010-04-04 06:43:15 -04:00
return ret ;
}
int
player_volume_set ( int vol )
{
2016-05-15 00:25:56 -04:00
union player_arg cmdarg ;
2010-04-04 06:43:15 -04:00
int ret ;
2016-05-15 00:25:56 -04:00
cmdarg . intval = vol ;
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
ret = commands_exec_sync ( cmdbase , volume_set , NULL , & cmdarg ) ;
2010-04-04 06:43:15 -04:00
return ret ;
}
2010-11-19 16:51:46 -05:00
int
player_volume_setrel_speaker ( uint64_t id , int relvol )
{
2016-05-15 00:25:56 -04:00
union player_arg cmdarg ;
2010-11-19 16:51:46 -05:00
int ret ;
2016-05-15 00:25:56 -04:00
cmdarg . vol_param . spk_id = id ;
cmdarg . vol_param . volume = relvol ;
2010-11-19 16:51:46 -05:00
2016-05-15 00:25:56 -04:00
ret = commands_exec_sync ( cmdbase , volume_setrel_speaker , NULL , & cmdarg ) ;
2010-11-19 16:51:46 -05:00
return ret ;
}
int
player_volume_setabs_speaker ( uint64_t id , int vol )
{
2016-05-15 00:25:56 -04:00
union player_arg cmdarg ;
2010-11-19 16:51:46 -05:00
int ret ;
2016-05-15 00:25:56 -04:00
cmdarg . vol_param . spk_id = id ;
cmdarg . vol_param . volume = vol ;
2010-11-19 16:51:46 -05:00
2016-05-15 00:25:56 -04:00
ret = commands_exec_sync ( cmdbase , volume_setabs_speaker , NULL , & cmdarg ) ;
2010-11-19 16:51:46 -05:00
return ret ;
}
2010-04-04 06:43:15 -04:00
int
player_repeat_set ( enum repeat_mode mode )
{
2016-05-15 00:25:56 -04:00
union player_arg cmdarg ;
2010-04-04 06:43:15 -04:00
int ret ;
2016-05-15 00:25:56 -04:00
cmdarg . mode = mode ;
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
ret = commands_exec_sync ( cmdbase , repeat_set , NULL , & cmdarg ) ;
2010-04-04 06:43:15 -04:00
return ret ;
}
int
player_shuffle_set ( int enable )
{
2016-05-15 00:25:56 -04:00
union player_arg cmdarg ;
2010-04-04 06:43:15 -04:00
int ret ;
2016-05-15 00:25:56 -04:00
cmdarg . intval = enable ;
2010-04-04 06:43:15 -04:00
2016-05-15 00:25:56 -04:00
ret = commands_exec_sync ( cmdbase , shuffle_set , NULL , & cmdarg ) ;
2010-04-04 06:43:15 -04:00
return ret ;
}
2014-05-17 08:06:50 -04:00
void
2015-08-08 12:02:49 -04:00
player_queue_clear_history ( )
2014-05-17 08:06:50 -04:00
{
2016-05-15 00:25:56 -04:00
commands_exec_sync ( cmdbase , playerqueue_clear_history , NULL , NULL ) ;
2014-05-17 08:06:50 -04:00
}
2010-07-31 06:30:51 -04:00
void
player_queue_plid ( uint32_t plid )
{
2016-05-15 00:25:56 -04:00
union player_arg cmdarg ;
2010-07-31 06:30:51 -04:00
2016-05-15 00:25:56 -04:00
cmdarg . id = plid ;
2010-07-31 06:30:51 -04:00
2016-05-15 00:25:56 -04:00
commands_exec_sync ( cmdbase , playerqueue_plid , NULL , & cmdarg ) ;
2010-07-31 06:30:51 -04:00
}
2010-11-19 14:20:37 -05:00
/* Non-blocking commands used by mDNS */
2016-01-23 19:14:07 -05:00
int
player_device_add ( void * device )
2010-09-30 13:24:34 -04:00
{
2016-05-15 00:25:56 -04:00
union player_arg * cmdarg ;
2010-11-02 12:55:26 -04:00
int ret ;
2010-09-30 13:24:34 -04:00
2016-05-15 00:25:56 -04:00
cmdarg = calloc ( 1 , sizeof ( union player_arg ) ) ;
if ( ! cmdarg )
2010-09-30 13:24:34 -04:00
{
2010-11-19 14:20:37 -05:00
DPRINTF ( E_LOG , L_PLAYER , " Could not allocate player_command \n " ) ;
2016-01-23 19:14:07 -05:00
return - 1 ;
2010-09-30 13:24:34 -04:00
}
2016-05-15 00:25:56 -04:00
cmdarg - > device = device ;
2010-11-19 14:20:37 -05:00
2016-05-15 00:25:56 -04:00
ret = commands_exec_async ( cmdbase , device_add , cmdarg ) ;
return ret ;
2010-11-19 14:20:37 -05:00
}
2010-09-30 13:24:34 -04:00
2016-01-23 19:14:07 -05:00
int
player_device_remove ( void * device )
2010-11-19 14:20:37 -05:00
{
2016-05-15 00:25:56 -04:00
union player_arg * cmdarg ;
2010-11-19 14:20:37 -05:00
int ret ;
2010-09-30 13:24:34 -04:00
2016-05-15 00:25:56 -04:00
cmdarg = calloc ( 1 , sizeof ( union player_arg ) ) ;
if ( ! cmdarg )
2010-11-02 12:55:26 -04:00
{
2010-11-19 14:20:37 -05:00
DPRINTF ( E_LOG , L_PLAYER , " Could not allocate player_command \n " ) ;
2016-01-23 19:14:07 -05:00
return - 1 ;
2010-11-02 12:55:26 -04:00
}
2016-05-15 00:25:56 -04:00
cmdarg - > device = device ;
2010-11-19 14:20:37 -05:00
2016-05-15 00:25:56 -04:00
ret = commands_exec_async ( cmdbase , device_remove_family , cmdarg ) ;
return ret ;
2010-09-30 13:24:34 -04:00
}
2015-04-07 17:35:56 -04:00
/* Thread: worker */
static void
2015-04-09 15:04:35 -04:00
player_metadata_send ( struct player_metadata * pmd )
2015-04-07 17:35:56 -04:00
{
2016-05-15 00:25:56 -04:00
union player_arg cmdarg ;
2015-04-07 17:35:56 -04:00
2016-05-15 00:25:56 -04:00
cmdarg . pmd = pmd ;
2015-04-07 17:35:56 -04:00
2016-05-15 00:25:56 -04:00
commands_exec_sync ( cmdbase , metadata_send , NULL , & cmdarg ) ;
2015-04-07 17:35:56 -04:00
}
2010-04-04 06:43:15 -04:00
/* Thread: player */
static void *
player ( void * arg )
{
2016-01-23 19:14:07 -05:00
struct output_device * device ;
2010-04-04 06:43:15 -04:00
int ret ;
ret = db_perthread_init ( ) ;
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Error: DB init failed \n " ) ;
pthread_exit ( NULL ) ;
}
event_base_dispatch ( evbase_player ) ;
if ( ! player_exit )
DPRINTF ( E_LOG , L_PLAYER , " Player event loop terminated ahead of time! \n " ) ;
2010-08-04 13:01:53 -04:00
/* Save selected devices */
2010-11-19 14:48:33 -05:00
db_speaker_clear_all ( ) ;
2010-08-04 13:01:53 -04:00
2016-01-23 19:14:07 -05:00
for ( device = dev_list ; device ; device = device - > next )
2010-08-04 13:01:53 -04:00
{
2016-02-08 16:17:10 -05:00
ret = db_speaker_save ( device - > id , device - > selected , device - > volume , device - > name ) ;
2010-11-19 14:48:33 -05:00
if ( ret < 0 )
2016-01-23 19:14:07 -05:00
DPRINTF ( E_LOG , L_PLAYER , " Could not save state for %s device '%s' \n " , device - > type_name , device - > name ) ;
2010-08-04 13:01:53 -04:00
}
2010-04-04 06:43:15 -04:00
db_perthread_deinit ( ) ;
pthread_exit ( NULL ) ;
}
/* Thread: main */
int
player_init ( void )
{
2016-02-11 02:29:29 -05:00
uint64_t interval ;
2010-04-04 06:43:15 -04:00
uint32_t rnd ;
int ret ;
player_exit = 0 ;
2016-09-10 17:26:06 -04:00
speaker_autoselect = cfg_getbool ( cfg_getsec ( cfg , " general " ) , " speaker_autoselect " ) ;
2016-02-28 04:34:18 -05:00
clear_queue_on_stop_disabled = cfg_getbool ( cfg_getsec ( cfg , " mpd " ) , " clear_queue_on_stop_disable " ) ;
2010-04-04 06:43:15 -04:00
dev_list = NULL ;
2010-11-19 16:51:46 -05:00
master_volume = - 1 ;
2016-01-23 19:14:07 -05:00
output_sessions = 0 ;
2010-04-04 06:43:15 -04:00
cur_playing = NULL ;
cur_streaming = NULL ;
2010-07-31 06:30:51 -04:00
cur_plid = 0 ;
2015-05-03 04:34:49 -04:00
cur_plversion = 0 ;
2010-04-04 06:43:15 -04:00
player_state = PLAY_STOPPED ;
repeat = REPEAT_OFF ;
shuffle = 0 ;
2014-09-27 16:59:19 -04:00
history = ( struct player_history * ) calloc ( 1 , sizeof ( struct player_history ) ) ;
2014-04-19 11:18:20 -04:00
2011-02-17 10:21:35 -05:00
/*
* Determine if the resolution of the system timer is > or < the size
* of an audio packet . NOTE : this assumes the system clock resolution
* is less than one second .
*/
2014-02-08 16:59:07 -05:00
if ( clock_getres ( CLOCK_MONOTONIC , & timer_res ) < 0 )
2011-02-17 10:21:35 -05:00
{
DPRINTF ( E_LOG , L_PLAYER , " Could not get the system timer resolution. \n " ) ;
2014-02-08 16:59:07 -05:00
2011-02-17 10:21:35 -05:00
return - 1 ;
}
2014-09-27 16:59:19 -04:00
# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
/* FreeBSD will report a resolution of 1, but actually has a resolution
* larger than an audio packet
*/
if ( timer_res . tv_nsec = = 1 )
timer_res . tv_nsec = 2 * AIRTUNES_V2_STREAM_PERIOD ;
# endif
2016-02-11 02:29:29 -05:00
// Set the tick interval for the playback timer
interval = MAX ( timer_res . tv_nsec , AIRTUNES_V2_STREAM_PERIOD ) ;
tick_interval . tv_nsec = interval ;
2014-09-27 16:59:19 -04:00
2016-02-11 02:29:29 -05:00
// Create the playback timer
2014-09-27 16:59:19 -04:00
# if defined(__linux__)
2016-02-11 02:29:29 -05:00
pb_timer_fd = timerfd_create ( CLOCK_MONOTONIC , TFD_CLOEXEC | TFD_NONBLOCK ) ;
2014-09-27 16:59:19 -04:00
ret = pb_timer_fd ;
# else
ret = timer_create ( CLOCK_MONOTONIC , NULL , & pb_timer ) ;
2014-02-08 16:59:07 -05:00
# endif
2014-09-27 16:59:19 -04:00
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Could not create playback timer: %s \n " , strerror ( errno ) ) ;
return - 1 ;
}
2011-02-17 10:21:35 -05:00
2016-02-11 02:29:29 -05:00
// Random RTP time start
2010-04-04 06:43:15 -04:00
gcry_randomize ( & rnd , sizeof ( rnd ) , GCRY_STRONG_RANDOM ) ;
last_rtptime = ( ( uint64_t ) 1 < < 32 ) | rnd ;
audio_buf = evbuffer_new ( ) ;
if ( ! audio_buf )
{
DPRINTF ( E_LOG , L_PLAYER , " Could not allocate evbuffer for audio buffer \n " ) ;
2014-09-27 16:59:19 -04:00
goto audio_fail ;
2010-04-04 06:43:15 -04:00
}
evbase_player = event_base_new ( ) ;
if ( ! evbase_player )
{
DPRINTF ( E_LOG , L_PLAYER , " Could not create an event base \n " ) ;
goto evbase_fail ;
}
2015-10-19 15:15:29 -04:00
# if defined(__linux__)
2016-02-11 02:29:29 -05:00
pb_timer_ev = event_new ( evbase_player , pb_timer_fd , EV_READ | EV_PERSIST , player_playback_cb , NULL ) ;
2010-04-04 06:43:15 -04:00
# else
2016-02-11 02:29:29 -05:00
pb_timer_ev = event_new ( evbase_player , SIGALRM , EV_SIGNAL | EV_PERSIST , player_playback_cb , NULL ) ;
2015-10-19 15:15:29 -04:00
# endif
2014-09-27 16:59:19 -04:00
if ( ! pb_timer_ev )
{
DPRINTF ( E_LOG , L_PLAYER , " Could not create playback timer event \n " ) ;
goto evnew_fail ;
}
event_add ( pb_timer_ev , NULL ) ;
2010-04-04 06:43:15 -04:00
2016-05-23 00:41:43 -04:00
cmdbase = commands_base_new ( evbase_player , NULL ) ;
2016-05-15 00:25:56 -04:00
2016-01-23 19:14:07 -05:00
ret = outputs_init ( ) ;
2010-04-04 06:43:15 -04:00
if ( ret < 0 )
{
2016-01-23 19:14:07 -05:00
DPRINTF ( E_FATAL , L_PLAYER , " Output initiation failed \n " ) ;
goto outputs_fail ;
2010-04-04 06:43:15 -04:00
}
ret = pthread_create ( & tid_player , NULL , player , NULL ) ;
if ( ret < 0 )
{
2016-01-23 19:14:07 -05:00
DPRINTF ( E_FATAL , L_PLAYER , " Could not spawn player thread: %s \n " , strerror ( errno ) ) ;
2010-04-04 06:43:15 -04:00
goto thread_fail ;
}
2016-05-05 13:39:15 -04:00
# if defined(HAVE_PTHREAD_SETNAME_NP)
2016-03-05 07:19:55 -05:00
pthread_setname_np ( tid_player , " player " ) ;
2016-05-05 13:39:15 -04:00
# elif defined(HAVE_PTHREAD_SET_NAME_NP)
2016-03-05 07:19:55 -05:00
pthread_set_name_np ( tid_player , " player " ) ;
# endif
2010-04-04 06:43:15 -04:00
return 0 ;
thread_fail :
2016-01-23 19:14:07 -05:00
outputs_deinit ( ) ;
outputs_fail :
2016-05-15 00:25:56 -04:00
commands_base_free ( cmdbase ) ;
2014-09-27 16:59:19 -04:00
evnew_fail :
2010-04-04 06:43:15 -04:00
event_base_free ( evbase_player ) ;
evbase_fail :
evbuffer_free ( audio_buf ) ;
2014-09-27 16:59:19 -04:00
audio_fail :
2014-09-27 17:05:22 -04:00
# if defined(__linux__)
2014-09-27 16:59:19 -04:00
close ( pb_timer_fd ) ;
2014-09-27 17:05:22 -04:00
# else
timer_delete ( pb_timer ) ;
# endif
2010-04-04 06:43:15 -04:00
return - 1 ;
}
/* Thread: main */
void
player_deinit ( void )
{
int ret ;
2016-05-23 00:41:43 -04:00
player_exit = 1 ;
2016-06-11 04:00:23 -04:00
commands_base_destroy ( cmdbase ) ;
2010-04-04 06:43:15 -04:00
ret = pthread_join ( tid_player , NULL ) ;
if ( ret ! = 0 )
{
2016-05-23 00:41:43 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Could not join player thread: %s \n " , strerror ( errno ) ) ;
2010-04-04 06:43:15 -04:00
return ;
}
2014-04-19 11:18:20 -04:00
free ( history ) ;
2014-09-27 16:59:19 -04:00
pb_timer_stop ( ) ;
2014-09-27 17:05:22 -04:00
# if defined(__linux__)
2014-09-27 16:59:19 -04:00
close ( pb_timer_fd ) ;
2014-09-27 17:05:22 -04:00
# else
timer_delete ( pb_timer ) ;
# endif
2014-09-27 16:59:19 -04:00
2010-04-04 06:43:15 -04:00
evbuffer_free ( audio_buf ) ;
2016-01-23 19:14:07 -05:00
outputs_deinit ( ) ;
2010-04-04 06:43:15 -04:00
2016-05-21 00:48:02 -04:00
event_base_free ( evbase_player ) ;
2010-04-04 06:43:15 -04:00
}