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>
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"
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"
2016-01-17 14:59:16 -05:00
# include "laudio.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
2016-02-11 02:29:29 -05:00
// Skips ticks for about 2 secs (seems to bring us back in sync for about 20 min)
# define PLAYER_TICKS_SKIP 126
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 ;
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-04-04 06:43:15 -04:00
enum player_sync_source
{
PLAYER_SYNC_CLOCK ,
PLAYER_SYNC_LAUDIO ,
} ;
2010-11-19 16:51:46 -05:00
struct volume_param {
int volume ;
uint64_t spk_id ;
} ;
2010-09-13 12:43:11 -04:00
struct player_command ;
typedef int ( * cmd_func ) ( struct player_command * cmd ) ;
2010-04-04 06:43:15 -04:00
2010-12-02 13:51:08 -05:00
struct spk_enum
{
spk_enum_cb cb ;
void * arg ;
} ;
2015-08-08 12:02:49 -04:00
struct playback_start_param
2014-12-21 14:41:44 -05:00
{
uint32_t id ;
2015-08-08 12:02:49 -04:00
int pos ;
2014-12-21 14:41:44 -05:00
uint32_t * id_ptr ;
} ;
2015-08-01 05:43:51 -04:00
struct playerqueue_get_param
{
int pos ;
int count ;
2015-10-03 02:24:13 -04:00
struct queue * queue ;
2015-08-08 12:02:49 -04:00
} ;
struct playerqueue_add_param
{
struct queue_item * items ;
int pos ;
2015-08-01 05:43:51 -04:00
} ;
2015-10-30 02:11:34 -04:00
struct playerqueue_move_param
{
uint32_t item_id ;
int from_pos ;
int to_pos ;
int count ;
} ;
2015-11-22 06:04:35 -05:00
struct playerqueue_remove_param
{
int from_pos ;
int count ;
} ;
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
} ;
2010-04-04 06:43:15 -04:00
struct player_command
{
2010-09-13 12:43:11 -04:00
pthread_mutex_t lck ;
pthread_cond_t cond ;
2010-04-04 06:43:15 -04:00
cmd_func func ;
cmd_func func_bh ;
2010-09-13 13:08:29 -04:00
2010-11-19 13:26:29 -05:00
int nonblock ;
2010-09-13 13:08:29 -04:00
union {
2010-11-19 16:51:46 -05:00
struct volume_param vol_param ;
2010-09-13 13:08:29 -04:00
void * noarg ;
2010-12-02 13:51:08 -05:00
struct spk_enum * spk_enum ;
2016-01-23 19:14:07 -05:00
struct output_device * device ;
2010-09-13 13:08:29 -04:00
struct player_status * status ;
struct player_source * ps ;
2015-04-09 15:04:35 -04:00
struct player_metadata * pmd ;
2010-09-13 13:08:29 -04:00
uint32_t * id_ptr ;
2016-01-23 19:14:07 -05:00
uint64_t * device_ids ;
2010-09-13 13:08:29 -04:00
enum repeat_mode mode ;
uint32_t id ;
int intval ;
2015-04-09 16:22:42 -04:00
struct icy_artwork icy ;
2015-08-08 12:02:49 -04:00
struct playback_start_param playback_start_param ;
2015-08-01 05:43:51 -04:00
struct playerqueue_get_param queue_get_param ;
2015-08-08 12:02:49 -04:00
struct playerqueue_add_param queue_add_param ;
2015-10-30 02:11:34 -04:00
struct playerqueue_move_param queue_move_param ;
2015-11-22 06:04:35 -05:00
struct playerqueue_remove_param queue_remove_param ;
2010-09-13 13:08:29 -04:00
} arg ;
2010-04-04 06:43:15 -04:00
int ret ;
2016-02-08 16:17:10 -05:00
int output_requests_pending ;
2010-04-04 06:43:15 -04:00
} ;
struct event_base * evbase_player ;
static int exit_pipe [ 2 ] ;
2010-09-13 12:43:11 -04:00
static int cmd_pipe [ 2 ] ;
2010-04-04 06:43:15 -04:00
static int player_exit ;
2014-09-27 16:59:19 -04:00
static struct event * exitev ;
static struct event * cmdev ;
2010-04-04 06:43:15 -04:00
static pthread_t tid_player ;
/* 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 source */
static enum player_sync_source pb_sync_source ;
/* 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 */
2015-09-18 13:19:02 -04:00
static int dev_autoselect ; //TODO [player] Is this still necessary?
2016-01-23 19:14:07 -05:00
static struct output_device * dev_list ;
2010-04-04 06:43:15 -04:00
2015-10-09 17:58:27 -04:00
/* Output status */
2010-04-04 06:43:15 -04:00
static enum laudio_state laudio_status ;
static int laudio_selected ;
2010-11-19 15:22:55 -05:00
static int laudio_volume ;
2010-11-19 16:51:46 -05:00
static int laudio_relvol ;
2016-01-23 19:14:07 -05:00
static int output_sessions ;
2015-10-09 17:58:27 -04:00
static int streaming_selected ;
static player_streaming_cb streaming_write ;
2010-04-04 06:43:15 -04:00
/* Commands */
2010-09-13 12:43:11 -04:00
static struct player_command * cur_cmd ;
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 ;
2010-04-04 06:43:15 -04:00
static struct evbuffer * audio_buf ;
2015-08-08 12:02:49 -04:00
/* Play queue */
static struct queue * queue ;
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
/* Command helpers */
static void
command_async_end ( struct player_command * cmd )
{
cur_cmd = NULL ;
pthread_cond_signal ( & cmd - > cond ) ;
pthread_mutex_unlock ( & cmd - > lck ) ;
/* Process commands again */
2014-09-27 16:59:19 -04:00
event_add ( cmdev , NULL ) ;
2010-09-13 12:43:11 -04:00
}
static void
command_init ( struct player_command * cmd )
{
memset ( cmd , 0 , sizeof ( struct player_command ) ) ;
pthread_mutex_init ( & cmd - > lck , NULL ) ;
pthread_cond_init ( & cmd - > cond , NULL ) ;
}
static void
command_deinit ( struct player_command * cmd )
{
pthread_cond_destroy ( & cmd - > cond ) ;
pthread_mutex_destroy ( & cmd - > lck ) ;
}
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-11-19 14:31:41 -05:00
if ( status = = PLAY_PLAYING )
dev_autoselect = 0 ;
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 ;
if ( laudio_selected )
laudio_relvol = vol_to_rel ( laudio_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 )
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 ;
if ( laudio_selected )
newmaster = laudio_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 & & ( 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
speaker_select_laudio ( void )
{
laudio_selected = 1 ;
2010-11-19 16:51:46 -05:00
if ( laudio_volume > master_volume )
{
2015-09-27 09:24:04 -04:00
if ( player_state = = PLAY_STOPPED | | master_volume = = - 1 )
2010-11-19 16:51:46 -05:00
volume_master_update ( laudio_volume ) ;
else
laudio_volume = master_volume ;
}
laudio_relvol = vol_to_rel ( laudio_volume ) ;
2010-11-21 04:05:41 -05:00
}
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
speaker_deselect_laudio ( void )
{
laudio_selected = 0 ;
2010-11-19 16:51:46 -05:00
if ( laudio_volume = = master_volume )
volume_master_find ( ) ;
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
}
2010-04-04 06:43:15 -04:00
static int
player_get_current_pos_clock ( uint64_t * pos , struct timespec * ts , int commit )
{
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 ;
}
static int
player_get_current_pos_laudio ( uint64_t * pos , struct timespec * ts , int commit )
{
int ret ;
* pos = laudio_get_pos ( ) ;
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 ;
}
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 " (laudio) \n " , * pos ) ;
2010-11-21 05:59:45 -05:00
# endif
2010-04-04 06:43:15 -04:00
}
return 0 ;
}
int
player_get_current_pos ( uint64_t * pos , struct timespec * ts , int commit )
{
switch ( pb_sync_source )
{
case PLAYER_SYNC_CLOCK :
return player_get_current_pos_clock ( pos , ts , commit ) ;
case PLAYER_SYNC_LAUDIO :
return player_get_current_pos_laudio ( pos , ts , commit ) ;
}
return - 1 ;
}
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
2014-07-13 04:09:05 -04:00
static int
2015-08-08 01:01:45 -04:00
playerqueue_clear ( struct player_command * cmd ) ;
2014-07-13 04:09:05 -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
2010-04-04 06:43:15 -04:00
static void
player_laudio_status_cb ( enum laudio_state status )
{
struct timespec ts ;
uint64_t pos ;
switch ( status )
{
/* Switch sync to clock sync */
case LAUDIO_STOPPING :
DPRINTF ( E_DBG , L_PLAYER , " Local audio stopping \n " ) ;
laudio_status = status ;
/* Synchronize pb_pos and pb_pos_stamp before laudio stops entirely */
player_get_current_pos_laudio ( & pos , & ts , 1 ) ;
pb_sync_source = PLAYER_SYNC_CLOCK ;
break ;
/* Switch sync to laudio sync */
case LAUDIO_RUNNING :
DPRINTF ( E_DBG , L_PLAYER , " Local audio running \n " ) ;
laudio_status = status ;
pb_sync_source = PLAYER_SYNC_LAUDIO ;
break ;
case LAUDIO_FAILED :
DPRINTF ( E_DBG , L_PLAYER , " Local audio failed \n " ) ;
pb_sync_source = PLAYER_SYNC_CLOCK ;
laudio_close ( ) ;
2016-01-23 19:14:07 -05:00
if ( output_sessions = = 0 )
2010-12-12 04:12:39 -05:00
playback_abort ( ) ;
2010-04-04 06:43:15 -04:00
2010-11-21 04:05:41 -05:00
speaker_deselect_laudio ( ) ;
2010-04-04 06:43:15 -04:00
break ;
default :
laudio_status = status ;
break ;
}
}
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 ;
db_file_update_icy ( metadata - > id , metadata - > artist , metadata - > title ) ;
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 ;
2015-03-31 17:05:24 -04:00
metadata - > id = cur_streaming - > id ;
/* 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
stream_setup ( struct player_source * ps , struct media_file_info * mfi )
2010-04-04 06:43:15 -04:00
{
2015-09-02 12:31:43 -04:00
char * url ;
int ret ;
if ( ! ps | | ! mfi )
{
DPRINTF ( E_LOG , L_PLAYER , " No player source and/or media info given to stream_setup \n " ) ;
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 :
2015-10-09 17:58:27 -04:00
ps - > xcode = transcode_setup ( mfi , XCODE_PCM16_NOHEADER , NULL ) ;
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 :
2015-09-02 12:31:43 -04:00
ret = http_stream_setup ( & url , mfi - > path ) ;
if ( ret < 0 )
break ;
free ( mfi - > path ) ;
mfi - > path = url ;
2015-10-09 17:58:27 -04:00
ps - > xcode = transcode_setup ( mfi , XCODE_PCM16_NOHEADER , NULL ) ;
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
2015-09-12 01:45:31 -04:00
ret = spotify_playback_setup ( mfi ) ;
2015-09-02 12:31:43 -04:00
# else
2015-09-13 01:58:27 -04: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' (%s) \n " ,
ps - > data_kind , mfi - > title , mfi - > 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 :
2015-09-02 12:31:43 -04:00
ret = pipe_setup ( mfi ) ;
2014-03-31 07:10:18 -04:00
break ;
2015-09-02 12:31:43 -04:00
default :
2015-09-13 01:58:27 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Unknown data kind (%d) for player source - cannot setup source '%s' (%s) \n " ,
ps - > data_kind , mfi - > title , mfi - > 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 *
2015-10-03 02:24:13 -04:00
source_new ( struct queue_item * item )
2015-09-02 12:31:43 -04:00
{
struct player_source * ps ;
ps = ( struct player_source * ) calloc ( 1 , sizeof ( struct player_source ) ) ;
2015-10-03 02:24:13 -04:00
ps - > id = queueitem_id ( item ) ;
2015-10-03 03:01:26 -04:00
ps - > item_id = queueitem_item_id ( item ) ;
2015-10-03 02:24:13 -04:00
ps - > data_kind = queueitem_data_kind ( item ) ;
ps - > media_kind = queueitem_media_kind ( item ) ;
ps - > len_ms = queueitem_len ( item ) ;
2015-09-02 12:31:43 -04:00
ps - > play_next = NULL ;
return ps ;
}
/*
* 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 ;
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 ;
struct media_file_info * mfi ;
uint64_t seek_frames ;
int seek_ms ;
int ret ;
ps_playing = source_now_playing ( ) ;
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 ;
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
{
2015-09-02 12:31:43 -04:00
mfi = db_file_fetch_byid ( cur_streaming - > id ) ;
if ( ! mfi )
{
DPRINTF ( E_LOG , L_PLAYER , " Couldn't fetch file id %d \n " , cur_streaming - > id ) ;
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
return - 1 ;
}
if ( mfi - > disabled )
{
DPRINTF ( E_DBG , L_PLAYER , " File id %d is disabled, skipping \n " , cur_streaming - > id ) ;
free_mfi ( mfi , 0 ) ;
return - 1 ;
}
DPRINTF ( E_INFO , L_PLAYER , " Opening '%s' (%s) \n " , mfi - > title , mfi - > path ) ;
2015-09-13 01:58:27 -04:00
ret = stream_setup ( cur_streaming , mfi ) ;
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Failed to open '%s' (%s) \n " , mfi - > title , mfi - > path ) ;
free_mfi ( mfi , 0 ) ;
return - 1 ;
}
free_mfi ( mfi , 0 ) ;
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 ) ;
return ret ;
}
/*
* Initializes playback of the given queue item ( but does not start playback )
*
* A new source is created for the given queue item and is set as the current
* streaming source . If a streaming source already existed ( and reached eof )
* the new source is appended as the play - next item to it .
*
* Stream - start and output - start values are set to the given start position .
*/
static int
2015-10-03 02:24:13 -04:00
source_open ( struct queue_item * qii , uint64_t start_pos , int seek )
2015-09-02 12:31:43 -04:00
{
struct player_source * ps ;
struct media_file_info * mfi ;
2015-10-03 02:24:13 -04:00
uint32_t id ;
2015-09-02 12:31:43 -04:00
int ret ;
if ( cur_streaming & & cur_streaming - > end = = 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Current streaming source not at eof %d \n " , cur_streaming - > id ) ;
return - 1 ;
2010-04-04 06:43:15 -04:00
}
2015-10-03 02:24:13 -04:00
id = queueitem_id ( qii ) ;
mfi = db_file_fetch_byid ( id ) ;
2015-09-02 12:31:43 -04:00
if ( ! mfi )
2010-04-04 06:43:15 -04:00
{
2015-10-03 02:24:13 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Couldn't fetch file id %d \n " , id ) ;
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
return - 1 ;
2010-04-04 06:43:15 -04:00
}
2015-09-02 12:31:43 -04:00
if ( mfi - > disabled )
2010-04-04 06:43:15 -04:00
{
2015-10-03 02:24:13 -04:00
DPRINTF ( E_DBG , L_PLAYER , " File id %d is disabled, skipping \n " , id ) ;
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
free_mfi ( mfi , 0 ) ;
2010-04-04 06:43:15 -04:00
return - 1 ;
}
2015-09-02 12:31:43 -04:00
DPRINTF ( E_INFO , L_PLAYER , " Opening '%s' (%s) \n " , mfi - > title , mfi - > path ) ;
ps = source_new ( qii ) ;
ret = stream_setup ( ps , mfi ) ;
2015-09-13 01:58:27 -04:00
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Failed to open '%s' (%s) \n " , mfi - > title , mfi - > path ) ;
free_mfi ( mfi , 0 ) ;
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 ;
2015-09-13 01:58:27 -04:00
/* Seek to the saved seek position */
2015-09-02 12:31:43 -04:00
if ( seek & & mfi - > seek )
source_seek ( mfi - > seek ) ;
free_mfi ( mfi , 0 ) ;
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 ;
}
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 ;
2015-09-02 12:31:43 -04:00
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 ;
}
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 ;
2015-10-03 02:24:13 -04:00
struct queue_item * item ;
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 " ) ;
2015-10-03 03:01:26 -04:00
item = queue_next ( queue , cur_streaming - > item_id , shuffle , repeat , 1 ) ;
2015-09-18 14:01:20 -04:00
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Error reading source %d \n " , cur_streaming - > id ) ;
2015-10-03 03:01:26 -04:00
queue_remove_byitemid ( queue , cur_streaming - > item_id ) ;
2015-09-18 14:01:20 -04:00
}
if ( item )
{
ret = source_open ( item , cur_streaming - > end + 1 , 0 ) ;
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
2010-05-01 13:24:06 -04:00
playback_write ( void )
2010-04-04 06:43:15 -04:00
{
2014-09-19 16:14:31 -04:00
uint8_t rawbuf [ STOB ( AIRTUNES_V2_PACKET_SAMPLES ) ] ;
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 ;
memset ( rawbuf , 0 , sizeof ( rawbuf ) ) ;
2010-12-12 04:00:43 -05: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:12:39 -05:00
playback_abort ( ) ;
2010-12-12 04:00:43 -05:00
return ;
}
2010-04-04 06:43:15 -04:00
2015-10-09 17:58:27 -04:00
if ( streaming_selected )
streaming_write ( rawbuf , sizeof ( rawbuf ) ) ;
2010-04-04 06:43:15 -04:00
if ( laudio_status & LAUDIO_F_STARTED )
laudio_write ( rawbuf , last_rtptime ) ;
2016-01-23 19:14:07 -05:00
if ( output_sessions > 0 )
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
uint32_t packet_send_count ;
int ret ;
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
// network stream. We might be consuming faster than the stream delivers, so
// 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
// from the stream server. In that situation we will skip reading data every
// second tick until we have skipt PLAYER_TICKS_SKIP ticks. That should make
// the source catch up. RTP destinations should be able to handle this
// gracefully if we just give them an rtptime that lets them know that some
// packets were "lost".
2016-02-11 14:02:17 -05:00
if ( overrun > PLAYER_TICKS_MAX_OVERRUN )
2016-02-11 02:29:29 -05:00
{
2016-02-11 14:02:17 -05:00
DPRINTF ( E_WARN , L_PLAYER , " Behind the playback timer with % " PRIu64 " ticks, initiating catch up \n " , overrun ) ;
2016-02-11 02:29:29 -05:00
ticks_skip = 2 * PLAYER_TICKS_SKIP + 1 ;
}
else if ( ticks_skip > 0 )
ticks_skip - - ;
// 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 ) ;
packet_send_count = 0 ;
2011-02-17 10:21:35 -05:00
do
{
2016-02-11 02:29:29 -05:00
// Skip reading and writing every second tick if we are behind the source
if ( ticks_skip % 2 = = 0 )
playback_write ( ) ;
else
last_rtptime + = AIRTUNES_V2_PACKET_SAMPLES ;
2011-02-17 10:21:35 -05:00
packet_timer_last = timespec_add ( packet_timer_last , packet_time ) ;
packet_send_count + + ;
/* not possible to have more than 126 audio packets per second */
2014-02-08 16:59:07 -05:00
if ( packet_send_count > 126 )
{
DPRINTF ( E_LOG , L_PLAYER , " Timing error detected during playback! Aborting. \n " ) ;
playback_abort ( ) ;
return ;
}
2011-02-17 10:21:35 -05:00
}
2014-09-27 16:59:19 -04:00
while ( timespec_cmp ( packet_timer_last , next_tick ) < 0 ) ;
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 */
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-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-01-23 19:14:07 -05:00
if ( device - > selected )
speaker_deselect_output ( remove ) ;
2010-11-21 04:54:55 -05:00
2010-11-19 14:48:33 -05:00
/* Save device volume */
2016-02-08 16:17:10 -05:00
ret = db_speaker_save ( remove - > id , 0 , remove - > volume , remove - > 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 " , remove - > type_name , remove - > name ) ;
2010-11-19 14:48:33 -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
}
2010-11-19 14:20:37 -05:00
static int
device_add ( struct player_command * cmd )
2010-11-19 13:13:30 -05:00
{
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-01-23 19:14:07 -05:00
add = cmd - > arg . 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
2010-11-19 14:48:33 -05:00
if ( dev_autoselect & & selected )
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
}
return 0 ;
}
static int
device_remove_family ( struct player_command * cmd )
{
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-01-23 19:14:07 -05:00
remove = cmd - > arg . 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 ) ;
2010-11-19 14:20:37 -05:00
return 0 ;
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
2010-11-19 14:20:37 -05:00
return 0 ;
2010-11-19 13:13:30 -05:00
}
2015-04-07 17:35:56 -04:00
static int
metadata_send ( struct player_command * cmd )
{
2015-04-09 15:04:35 -04:00
struct player_metadata * pmd ;
2015-04-07 17:35:56 -04:00
2015-04-09 15:04:35 -04:00
pmd = cmd - > arg . 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
return 0 ;
}
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-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-02-08 16:17:10 -05:00
cur_cmd - > output_requests_pending - - ;
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
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-02-08 16:17:10 -05:00
if ( cur_cmd - > output_requests_pending = = 0 )
2010-04-04 06:43:15 -04:00
{
2010-09-13 12:43:11 -04:00
if ( cur_cmd - > func_bh )
cur_cmd - > ret = cur_cmd - > func_bh ( cur_cmd ) ;
2010-04-04 06:43:15 -04:00
else
2010-09-13 12:43:11 -04:00
cur_cmd - > ret = 0 ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
command_async_end ( cur_cmd ) ;
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
{
int ret ;
2016-02-08 16:17:10 -05:00
cur_cmd - > output_requests_pending - - ;
2010-04-04 06:43:15 -04:00
2016-01-23 19:14:07 -05:00
if ( output_sessions )
output_sessions - - ;
2010-04-04 06:43:15 -04:00
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
2010-09-13 12:43:11 -04:00
if ( cur_cmd - > ret ! = - 2 )
cur_cmd - > ret = - 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-02-08 16:17:10 -05:00
if ( cur_cmd - > output_requests_pending = = 0 )
2010-04-04 06:43:15 -04:00
{
2010-09-13 12:43:11 -04:00
/* cur_cmd->ret already set
2010-04-04 06:43:15 -04:00
* - to 0 ( or - 2 if password issue ) in speaker_set ( )
* - to - 1 above on error
*/
2010-09-13 12:43:11 -04:00
command_async_end ( cur_cmd ) ;
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
{
/* 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 ;
int ret ;
2016-02-08 16:17:10 -05:00
cur_cmd - > output_requests_pending - - ;
2010-04-04 06:43:15 -04:00
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
2010-09-13 12:43:11 -04:00
if ( cur_cmd - > ret ! = - 2 )
cur_cmd - > ret = - 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 ;
2010-09-13 12:43:11 -04:00
cur_cmd - > ret = - 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
2010-09-13 12:43:11 -04:00
if ( cur_cmd - > ret ! = - 2 )
cur_cmd - > ret = - 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-02-08 16:17:10 -05:00
if ( cur_cmd - > output_requests_pending = = 0 )
2010-04-04 06:43:15 -04:00
{
2010-09-13 12:43:11 -04:00
/* cur_cmd->ret already set
2010-04-04 06:43:15 -04:00
* - to 0 in speaker_set ( ) ( default )
* - to - 2 above if password issue
* - to - 1 above on error
*/
2010-09-13 12:43:11 -04:00
command_async_end ( cur_cmd ) ;
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
{
int ret ;
2016-02-08 16:17:10 -05:00
cur_cmd - > output_requests_pending - - ;
2010-04-04 06:43:15 -04:00
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
2010-09-13 12:43:11 -04:00
if ( cur_cmd - > ret ! = - 2 )
cur_cmd - > ret = - 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 ;
2010-09-13 12:43:11 -04:00
cur_cmd - > ret = - 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
2010-09-13 12:43:11 -04:00
if ( cur_cmd - > ret ! = - 2 )
cur_cmd - > ret = - 1 ;
2010-04-04 06:43:15 -04:00
goto out ;
}
out :
2016-02-08 16:17:10 -05:00
if ( cur_cmd - > output_requests_pending = = 0 )
2010-04-04 06:43:15 -04:00
{
2010-09-13 12:43:11 -04:00
/* cur_cmd->ret already set
2010-04-04 06:43:15 -04:00
* - to 0 in speaker_set ( ) ( default )
* - to - 2 above if password issue
* - to - 1 above on error
*/
2010-09-13 12:43:11 -04:00
command_async_end ( cur_cmd ) ;
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-02-08 16:17:10 -05:00
cur_cmd - > output_requests_pending - - ;
2010-04-04 06:43:15 -04:00
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-02-08 16:17:10 -05:00
if ( cur_cmd - > output_requests_pending = = 0 )
2010-04-04 06:43:15 -04:00
{
2010-09-13 12:43:11 -04:00
cur_cmd - > ret = cur_cmd - > func_bh ( cur_cmd ) ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
command_async_end ( cur_cmd ) ;
2010-04-04 06:43:15 -04:00
}
}
2010-12-12 04:12:39 -05:00
/* Internal abort routine */
static void
playback_abort ( void )
{
if ( laudio_status ! = LAUDIO_CLOSED )
laudio_close ( ) ;
2016-01-23 19:14:07 -05:00
if ( output_sessions > 0 )
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
2015-08-08 12:02:49 -04:00
playerqueue_clear ( NULL ) ;
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
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 */
static int
2010-09-13 12:43:11 -04:00
get_status ( struct player_command * cmd )
2010-04-04 06:43:15 -04:00
{
struct timespec ts ;
struct player_source * ps ;
struct player_status * status ;
2015-10-03 02:24:13 -04:00
struct queue_item * item_next ;
2010-04-04 06:43:15 -04:00
uint64_t pos ;
int ret ;
2010-09-13 13:08:29 -04:00
status = cmd - > arg . 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 ;
2015-05-03 04:34:49 -04:00
status - > plversion = cur_plversion ;
2010-07-31 06:30:51 -04:00
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
2015-10-03 03:01:26 -04:00
status - > pos_pl = queue_index_byitemid ( queue , cur_streaming - > item_id , 0 ) ;
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 ;
status - > pos_pl = queue_index_byitemid ( queue , ps - > item_id , 0 ) ;
2014-12-21 14:41:44 -05:00
2015-10-03 03:01:26 -04:00
item_next = queue_next ( queue , ps - > item_id , shuffle , repeat , 0 ) ;
2015-08-08 12:02:49 -04:00
if ( item_next )
{
2015-10-03 02:24:13 -04:00
status - > next_id = queueitem_id ( item_next ) ;
2015-10-03 03:01:26 -04:00
status - > next_item_id = queueitem_item_id ( item_next ) ;
status - > next_pos_pl = queue_index_byitemid ( queue , status - > next_item_id , 0 ) ;
2015-08-08 12:02:49 -04:00
}
else
{
//TODO [queue/mpd] Check how mpd sets the next-id/-pos if the last song is playing
status - > next_id = 0 ;
status - > next_pos_pl = 0 ;
}
2014-12-21 14:41:44 -05:00
2015-08-08 12:02:49 -04:00
status - > playlistlength = queue_count ( queue ) ;
2010-04-04 06:43:15 -04:00
break ;
}
return 0 ;
}
static int
2010-09-13 12:43:11 -04:00
now_playing ( struct player_command * cmd )
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
2010-09-13 13:08:29 -04:00
id = cmd - > arg . 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
return - 1 ;
return 0 ;
}
2015-03-14 16:42:53 -04:00
static int
artwork_url_get ( struct player_command * cmd )
{
2015-04-09 16:33:23 -04:00
struct player_source * ps ;
2015-04-09 16:22:42 -04:00
cmd - > arg . 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
return - 1 ;
2015-04-09 16:22:42 -04:00
/* Check that we are playing a viable stream, and that it has the requested id */
2015-10-09 17:58:27 -04:00
if ( ! ps - > xcode | | ps - > data_kind ! = DATA_KIND_HTTP | | ps - > id ! = cmd - > arg . icy . id )
2015-03-14 16:42:53 -04:00
return - 1 ;
2015-10-09 17:58:27 -04:00
cmd - > arg . icy . artwork_url = transcode_metadata_artwork_url ( ps - > xcode ) ;
2015-03-14 16:42:53 -04:00
return 0 ;
}
2010-04-04 06:43:15 -04:00
static int
2010-09-13 12:43:11 -04:00
playback_stop ( struct player_command * cmd )
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
if ( laudio_status ! = LAUDIO_CLOSED )
laudio_close ( ) ;
/* 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-02-08 16:17:10 -05:00
cmd - > output_requests_pending = 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 ( ) ;
2010-12-12 04:25:21 -05:00
/* We're async if we need to flush RAOP devices */
2016-02-08 16:17:10 -05:00
if ( cmd - > output_requests_pending > 0 )
2010-12-12 04:25:21 -05:00
return 1 ; /* async */
2010-04-04 06:43:15 -04:00
return 0 ;
}
/* Playback startup bottom half */
static int
2010-09-13 12:43:11 -04:00
playback_start_bh ( struct player_command * cmd )
2010-04-04 06:43:15 -04:00
{
int ret ;
2016-01-23 19:14:07 -05:00
if ( ( laudio_status = = LAUDIO_CLOSED ) & & ( 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 ;
}
/* Start laudio first as it can fail, but can be stopped easily if needed */
if ( laudio_status = = LAUDIO_OPEN )
{
2010-11-19 15:22:55 -05:00
laudio_set_volume ( laudio_volume ) ;
2010-04-04 06:43:15 -04:00
ret = laudio_start ( pb_pos , last_rtptime + AIRTUNES_V2_PACKET_SAMPLES ) ;
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Local audio failed to start \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 */
if ( output_sessions > 0 )
outputs_playback_start ( last_rtptime + AIRTUNES_V2_PACKET_SAMPLES , & pb_pos_stamp ) ;
2010-04-04 06:43:15 -04:00
status_update ( PLAY_PLAYING ) ;
return 0 ;
out_fail :
2010-12-12 04:12:39 -05:00
playback_abort ( ) ;
2010-04-04 06:43:15 -04:00
return - 1 ;
}
static int
2015-10-03 02:24:13 -04:00
playback_start_item ( struct player_command * cmd , struct queue_item * qii )
2010-04-04 06:43:15 -04:00
{
2015-08-08 12:02:49 -04:00
uint32_t * dbmfi_id ;
2016-01-23 19:14:07 -05:00
struct output_device * device ;
2015-08-08 12:02:49 -04:00
struct player_source * ps_playing ;
2015-10-03 02:24:13 -04:00
struct queue_item * item ;
2010-04-04 06:43:15 -04:00
int ret ;
2015-08-08 12:02:49 -04:00
dbmfi_id = cmd - > arg . playback_start_param . id_ptr ;
ps_playing = source_now_playing ( ) ;
2010-04-04 06:43:15 -04:00
if ( player_state = = PLAY_PLAYING )
{
2014-12-21 14:41:44 -05:00
/*
* If player is already playing a song , only return current playing song id
* and do not change player state ( ignores given arguments for playing a
* specified song by pos or id ) .
*/
2015-08-08 12:02:49 -04:00
if ( dbmfi_id & & ps_playing )
2010-04-04 06:43:15 -04:00
{
2015-08-08 12:02:49 -04:00
* dbmfi_id = ps_playing - > id ;
2010-04-04 06:43:15 -04:00
}
status_update ( player_state ) ;
return 0 ;
}
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 ;
2015-09-02 12:31:43 -04:00
item = NULL ;
2015-08-08 12:02:49 -04:00
if ( qii )
2010-04-04 06:43:15 -04:00
{
2015-09-02 12:31:43 -04:00
item = qii ;
2010-04-04 06:43:15 -04:00
}
else if ( ! cur_streaming )
{
if ( shuffle )
2015-09-02 12:31:43 -04:00
queue_shuffle ( queue , 0 ) ;
2015-09-19 02:23:41 -04:00
item = queue_next ( queue , 0 , shuffle , repeat , 0 ) ;
2015-09-02 12:31:43 -04:00
}
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
if ( item )
{
source_stop ( ) ;
2015-09-20 01:11:39 -04:00
ret = source_open ( item , last_rtptime + AIRTUNES_V2_PACKET_SAMPLES , 1 ) ;
if ( ret < 0 )
{
playback_abort ( ) ;
return - 1 ;
}
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 ( ) ;
return - 1 ;
2011-03-26 13:33:19 -04:00
}
2010-04-04 06:43:15 -04:00
2015-09-02 12:31:43 -04:00
if ( dbmfi_id )
* dbmfi_id = cur_streaming - > id ;
2015-09-26 02:45:52 -04:00
metadata_trigger ( 1 ) ;
2015-09-02 12:31:43 -04:00
2010-04-04 06:43:15 -04:00
/* Start local audio if needed */
if ( laudio_selected & & ( laudio_status = = LAUDIO_CLOSED ) )
{
ret = laudio_open ( ) ;
if ( ret < 0 )
2015-01-16 17:18:21 -05:00
DPRINTF ( E_LOG , L_PLAYER , " Could not open local audio, will try AirPlay \n " ) ;
2010-04-04 06:43:15 -04:00
}
/* Start RAOP sessions on selected devices if needed */
2016-02-08 16:17:10 -05:00
cmd - > output_requests_pending = 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 ) ;
cmd - > output_requests_pending + + ;
2010-04-04 06:43:15 -04:00
}
}
2014-01-13 17:24:45 -05:00
/* Try to autoselect a non-selected RAOP device if the above failed */
2016-02-08 16:17:10 -05:00
if ( ( laudio_status = = LAUDIO_CLOSED ) & & ( cmd - > output_requests_pending = = 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-01-23 19:14:07 -05:00
if ( ! device - > session )
2014-01-13 17:24:45 -05:00
{
2016-01-23 19:14:07 -05:00
speaker_select_output ( device ) ;
ret = outputs_device_start ( device , device_restart_cb , last_rtptime + AIRTUNES_V2_PACKET_SAMPLES ) ;
2014-01-13 17:24:45 -05:00
if ( ret < 0 )
{
2016-01-23 19:14:07 -05:00
DPRINTF ( E_DBG , L_PLAYER , " Could not autoselect %s device '%s' \n " , device - > type_name , device - > name ) ;
speaker_deselect_output ( device ) ;
2014-01-13 17:24:45 -05:00
continue ;
}
2016-01-23 19:14:07 -05:00
DPRINTF ( E_INFO , L_PLAYER , " Autoselecting %s device '%s' \n " , device - > type_name , device - > name ) ;
2016-02-08 16:17:10 -05:00
cmd - > output_requests_pending + + ;
2014-01-13 17:24:45 -05:00
break ;
}
}
/* No luck finding valid output */
2016-02-08 16:17:10 -05:00
if ( ( laudio_status = = LAUDIO_CLOSED ) & & ( cmd - > output_requests_pending = = 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 ( ) ;
2010-04-04 06:43:15 -04:00
return - 1 ;
}
/* We're async if we need to start RAOP devices */
2016-02-08 16:17:10 -05:00
if ( cmd - > output_requests_pending > 0 )
2010-04-04 06:43:15 -04:00
return 1 ; /* async */
/* Otherwise, just run the bottom half */
2010-09-13 12:43:11 -04:00
return playback_start_bh ( cmd ) ;
2010-04-04 06:43:15 -04:00
}
2015-08-08 12:02:49 -04:00
static int
playback_start ( struct player_command * cmd )
{
return playback_start_item ( cmd , NULL ) ;
}
static int
playback_start_byitemid ( struct player_command * cmd )
{
int item_id ;
2015-10-03 02:24:13 -04:00
struct queue_item * qii ;
2015-08-08 12:02:49 -04:00
item_id = cmd - > arg . playback_start_param . id ;
qii = queue_get_byitemid ( queue , item_id ) ;
return playback_start_item ( cmd , qii ) ;
}
static int
playback_start_byindex ( struct player_command * cmd )
{
int pos ;
2015-10-03 02:24:13 -04:00
struct queue_item * qii ;
2015-08-08 12:02:49 -04:00
pos = cmd - > arg . playback_start_param . pos ;
qii = queue_get_byindex ( queue , pos , 0 ) ;
return playback_start_item ( cmd , qii ) ;
}
static int
playback_start_bypos ( struct player_command * cmd )
{
int offset ;
struct player_source * ps_playing ;
2015-10-03 02:24:13 -04:00
struct queue_item * qii ;
2015-08-08 12:02:49 -04:00
offset = cmd - > arg . playback_start_param . pos ;
ps_playing = source_now_playing ( ) ;
if ( ps_playing )
{
2015-10-03 03:01:26 -04:00
qii = queue_get_bypos ( queue , ps_playing - > item_id , offset , shuffle ) ;
2015-08-08 12:02:49 -04:00
}
else
{
qii = queue_get_byindex ( queue , offset , shuffle ) ;
}
return playback_start_item ( cmd , qii ) ;
}
2010-04-04 06:43:15 -04:00
static int
2010-09-13 12:43:11 -04:00
playback_prev_bh ( struct player_command * cmd )
2010-04-04 06:43:15 -04:00
{
int ret ;
2014-05-17 09:33:35 -04:00
int pos_sec ;
2015-10-03 02:24:13 -04:00
struct queue_item * item ;
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 " ) ;
return - 1 ;
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
{
2015-10-03 03:01:26 -04:00
item = queue_prev ( queue , cur_streaming - > item_id , shuffle , repeat ) ;
2015-09-02 12:31:43 -04:00
if ( ! item )
{
playback_abort ( ) ;
return - 1 ;
}
source_stop ( ) ;
ret = source_open ( item , 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
2014-05-17 09:33:35 -04:00
return - 1 ;
}
}
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 ( ) ;
return - 1 ;
}
2010-04-04 06:43:15 -04:00
}
if ( player_state = = PLAY_STOPPED )
return - 1 ;
/* Silent status change - playback_start() sends the real status update */
player_state = PLAY_PAUSED ;
return 0 ;
}
2015-09-20 01:11:39 -04:00
/*
* The bottom half of the next command
*/
2010-04-04 06:43:15 -04:00
static int
2010-09-13 12:43:11 -04:00
playback_next_bh ( struct player_command * cmd )
2010-04-04 06:43:15 -04:00
{
int ret ;
2015-10-03 02:24:13 -04:00
struct queue_item * item ;
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 " ) ;
return - 1 ;
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
2015-10-03 03:01:26 -04:00
item = queue_next ( queue , cur_streaming - > item_id , shuffle , repeat , 0 ) ;
2015-09-02 12:31:43 -04:00
if ( ! item )
{
playback_abort ( ) ;
return - 1 ;
}
source_stop ( ) ;
2014-05-03 23:45:53 -04:00
2015-09-02 12:31:43 -04:00
ret = source_open ( item , 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 ( ) ;
2010-04-04 06:43:15 -04:00
return - 1 ;
}
if ( player_state = = PLAY_STOPPED )
return - 1 ;
/* Silent status change - playback_start() sends the real status update */
player_state = PLAY_PAUSED ;
return 0 ;
}
static int
2010-09-13 12:43:11 -04:00
playback_seek_bh ( struct player_command * cmd )
2010-04-04 06:43:15 -04:00
{
int ms ;
int ret ;
2010-09-13 13:08:29 -04:00
ms = cmd - > arg . 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
return - 1 ;
}
/* Silent status change - playback_start() sends the real status update */
player_state = PLAY_PAUSED ;
return 0 ;
}
static int
2010-09-13 12:43:11 -04:00
playback_pause_bh ( struct player_command * cmd )
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 ( ) ;
return - 1 ;
}
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
}
return 0 ;
}
static int
2010-09-13 12:43:11 -04:00
playback_pause ( struct player_command * cmd )
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 ( ) ;
return - 1 ;
2010-04-04 06:43:15 -04:00
}
/* Make sure playback is still running after source_check() */
if ( player_state = = PLAY_STOPPED )
return - 1 ;
2016-02-08 16:17:10 -05:00
cmd - > output_requests_pending = outputs_flush ( device_command_cb , last_rtptime + AIRTUNES_V2_PACKET_SAMPLES ) ;
2010-04-04 06:43:15 -04:00
if ( laudio_status ! = LAUDIO_CLOSED )
laudio_stop ( ) ;
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 ( ) ;
2010-04-04 06:43:15 -04:00
/* We're async if we need to flush RAOP devices */
2016-02-08 16:17:10 -05:00
if ( cmd - > output_requests_pending > 0 )
2010-04-04 06:43:15 -04:00
return 1 ; /* async */
/* Otherwise, just run the bottom half */
2010-09-13 12:43:11 -04:00
return cmd - > func_bh ( cmd ) ;
2010-04-04 06:43:15 -04:00
}
2010-12-02 13:51:08 -05:00
static int
speaker_enumerate ( struct player_command * cmd )
{
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
char * laudio_name ;
spk_enum = cmd - > arg . spk_enum ;
laudio_name = cfg_getstr ( cfg_getsec ( cfg , " audio " ) , " nickname " ) ;
2016-01-23 19:14:07 -05:00
/* Auto-select local audio if there are no other output devices */
2010-11-21 04:05:41 -05:00
if ( ! dev_list & & ! laudio_selected )
speaker_select_laudio ( ) ;
2010-12-02 13:51:08 -05:00
2011-03-05 04:25:44 -05:00
flags . selected = laudio_selected ;
flags . has_password = 0 ;
2011-03-05 04:29:05 -05:00
flags . has_video = 0 ;
2011-03-05 04:25:44 -05:00
spk_enum - > cb ( 0 , laudio_name , laudio_relvol , flags , spk_enum - > arg ) ;
2010-11-19 16:51:46 -05:00
# ifdef DEBUG_RELVOL
DPRINTF ( E_DBG , L_PLAYER , " *** master: %d \n " , master_volume ) ;
DPRINTF ( E_DBG , L_PLAYER , " *** laudio: abs %d rel %d \n " , laudio_volume , laudio_relvol ) ;
# 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-01-23 19:14:07 -05:00
spk_enum - > cb ( device - > id , device - > name , device - > relvol , 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
}
return 0 ;
}
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
{
struct timespec ts ;
uint64_t pos ;
int ret ;
2016-01-23 19:14:07 -05:00
if ( ! device )
2010-04-04 06:43:15 -04:00
{
/* Local */
DPRINTF ( E_DBG , L_PLAYER , " Activating local audio \n " ) ;
if ( laudio_status = = LAUDIO_CLOSED )
{
ret = laudio_open ( ) ;
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Could not open local audio \n " ) ;
return - 1 ;
}
}
if ( player_state = = PLAY_PLAYING )
{
2010-11-19 15:22:55 -05:00
laudio_set_volume ( laudio_volume ) ;
2010-04-04 06:43:15 -04:00
ret = player_get_current_pos ( & pos , & ts , 0 ) ;
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Could not get current stream position for local audio start \n " ) ;
laudio_close ( ) ;
return - 1 ;
}
ret = laudio_start ( pos , last_rtptime + AIRTUNES_V2_PACKET_SAMPLES ) ;
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Local playback failed to start \n " ) ;
laudio_close ( ) ;
return - 1 ;
}
}
return 0 ;
}
else
{
if ( player_state = = PLAY_PLAYING )
{
2016-01-23 19:14:07 -05:00
DPRINTF ( E_DBG , L_PLAYER , " Activating %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
ret = outputs_device_start ( device , device_activate_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 %s device '%s' \n " , device - > type_name , device - > name ) ;
2010-04-04 06:43:15 -04:00
return - 1 ;
}
}
else
{
2016-01-23 19:14:07 -05: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-01-23 19:14:07 -05:00
ret = outputs_device_probe ( device , device_probe_cb ) ;
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 probe %s device '%s' \n " , device - > type_name , device - > name ) ;
2010-04-04 06:43:15 -04:00
return - 1 ;
}
}
2010-09-13 12:43:11 -04:00
return 1 ;
2010-04-04 06:43:15 -04:00
}
return - 1 ;
}
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-01-23 19:14:07 -05:00
if ( ! device )
2010-04-04 06:43:15 -04:00
{
/* Local */
DPRINTF ( E_DBG , L_PLAYER , " Deactivating local audio \n " ) ;
if ( laudio_status = = LAUDIO_CLOSED )
return 0 ;
if ( laudio_status & LAUDIO_F_STARTED )
laudio_stop ( ) ;
laudio_close ( ) ;
return 0 ;
}
else
{
2016-01-23 19:14:07 -05: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-01-23 19:14:07 -05:00
outputs_status_cb ( device - > session , device_shutdown_cb ) ;
outputs_device_stop ( device - > session ) ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
return 1 ;
2010-04-04 06:43:15 -04:00
}
return - 1 ;
}
static int
2010-09-13 12:43:11 -04:00
speaker_set ( struct player_command * cmd )
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
uint64_t * ids ;
int nspk ;
int i ;
int ret ;
2016-01-23 19:14:07 -05:00
ids = cmd - > arg . 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-02-08 16:17:10 -05:00
cmd - > output_requests_pending = 0 ;
2010-09-13 12:43:11 -04:00
cmd - > ret = 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
2010-09-13 12:43:11 -04:00
cmd - > ret = - 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
2010-09-13 12:43:11 -04:00
if ( cmd - > ret ! = - 2 )
cmd - > ret = - 1 ;
2010-04-04 06:43:15 -04:00
}
2010-09-13 12:43:11 -04:00
/* ret = 1 if RAOP needs to take action */
2016-02-08 16:17:10 -05:00
cmd - > output_requests_pending + = ret ;
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
2010-09-13 12:43:11 -04:00
if ( cmd - > ret ! = - 2 )
cmd - > ret = - 1 ;
2010-04-04 06:43:15 -04:00
}
2010-09-13 12:43:11 -04:00
/* ret = 1 if RAOP needs to take action */
2016-02-08 16:17:10 -05:00
cmd - > output_requests_pending + = ret ;
2010-04-04 06:43:15 -04:00
}
}
}
/* Local audio */
for ( i = 1 ; i < = nspk ; i + + )
{
if ( ids [ i ] = = 0 )
break ;
}
if ( i < = nspk )
{
DPRINTF ( E_DBG , L_PLAYER , " Local audio selected \n " ) ;
2010-11-21 04:05:41 -05:00
if ( ! laudio_selected )
speaker_select_laudio ( ) ;
2010-04-04 06:43:15 -04:00
if ( ! ( laudio_status & LAUDIO_F_STARTED ) )
{
ret = speaker_activate ( NULL ) ;
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Could not activate local audio output \n " ) ;
2010-11-21 04:05:41 -05:00
speaker_deselect_laudio ( ) ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
if ( cmd - > ret ! = - 2 )
cmd - > ret = - 1 ;
2010-04-04 06:43:15 -04:00
}
}
}
else
{
DPRINTF ( E_DBG , L_PLAYER , " Local audio NOT selected \n " ) ;
2010-11-21 04:05:41 -05:00
if ( laudio_selected )
speaker_deselect_laudio ( ) ;
2010-04-04 06:43:15 -04:00
if ( laudio_status ! = LAUDIO_CLOSED )
{
ret = speaker_deactivate ( NULL ) ;
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Could not deactivate local audio output \n " ) ;
2010-09-13 12:43:11 -04:00
if ( cmd - > ret ! = - 2 )
cmd - > ret = - 1 ;
2010-04-04 06:43:15 -04:00
}
}
}
2015-05-03 02:45:38 -04:00
listener_notify ( LISTENER_SPEAKER ) ;
2016-02-08 16:17:10 -05:00
if ( cmd - > output_requests_pending > 0 )
2010-04-04 06:43:15 -04:00
return 1 ; /* async */
2010-09-13 12:43:11 -04:00
return cmd - > ret ;
2010-04-04 06:43:15 -04:00
}
static int
2010-09-13 12:43:11 -04:00
volume_set ( struct player_command * cmd )
2010-04-04 06:43:15 -04:00
{
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
2010-11-19 16:51:46 -05:00
volume = cmd - > arg . intval ;
2010-04-04 06:43:15 -04:00
2010-11-19 16:51:46 -05:00
if ( master_volume = = volume )
return 0 ;
master_volume = volume ;
if ( laudio_selected )
{
laudio_volume = rel_to_vol ( laudio_relvol ) ;
laudio_set_volume ( laudio_volume ) ;
# ifdef DEBUG_RELVOL
DPRINTF ( E_DBG , L_PLAYER , " *** laudio: abs %d rel %d \n " , laudio_volume , laudio_relvol ) ;
# endif
}
2010-11-19 15:22:55 -05:00
2016-02-08 16:17:10 -05:00
cmd - > output_requests_pending = 0 ;
2010-11-19 15:49:17 -05:00
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-02-08 16:17:10 -05:00
cmd - > output_requests_pending + = 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-02-08 16:17:10 -05:00
if ( cmd - > output_requests_pending > 0 )
2010-04-04 06:43:15 -04:00
return 1 ; /* async */
return 0 ;
}
2010-11-19 16:51:46 -05:00
static int
volume_setrel_speaker ( struct player_command * cmd )
{
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 ;
id = cmd - > arg . vol_param . spk_id ;
relvol = cmd - > arg . vol_param . volume ;
if ( id = = 0 )
{
laudio_relvol = relvol ;
laudio_volume = rel_to_vol ( relvol ) ;
laudio_set_volume ( laudio_volume ) ;
# ifdef DEBUG_RELVOL
DPRINTF ( E_DBG , L_PLAYER , " *** laudio: abs %d rel %d \n " , laudio_volume , laudio_relvol ) ;
# endif
}
else
{
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 - > id ! = id )
2010-11-19 16:51:46 -05:00
continue ;
2016-01-23 19:14:07 -05:00
if ( ! device - > selected )
2010-11-19 16:51:46 -05:00
return 0 ;
2016-01-23 19:14:07 -05:00
device - > relvol = relvol ;
device - > volume = rel_to_vol ( 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
2016-01-23 19:14:07 -05:00
if ( device - > session )
2016-02-08 16:17:10 -05:00
cmd - > output_requests_pending = outputs_device_volume_set ( device , device_command_cb ) ;
2010-11-19 16:51:46 -05:00
break ;
}
}
2015-05-03 02:45:38 -04:00
listener_notify ( LISTENER_VOLUME ) ;
2016-02-08 16:17:10 -05:00
if ( cmd - > output_requests_pending > 0 )
2010-11-19 16:51:46 -05:00
return 1 ; /* async */
return 0 ;
}
static int
volume_setabs_speaker ( struct player_command * cmd )
{
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 ;
id = cmd - > arg . vol_param . spk_id ;
volume = cmd - > arg . vol_param . volume ;
master_volume = volume ;
if ( id = = 0 )
{
laudio_relvol = 100 ;
laudio_volume = volume ;
laudio_set_volume ( laudio_volume ) ;
}
else
laudio_relvol = vol_to_rel ( laudio_volume ) ;
# ifdef DEBUG_RELVOL
DPRINTF ( E_DBG , L_PLAYER , " *** laudio: abs %d rel %d \n " , laudio_volume , laudio_relvol ) ;
# endif
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-02-08 16:17:10 -05:00
cmd - > output_requests_pending = outputs_device_volume_set ( device , device_command_cb ) ;
2010-11-19 16:51:46 -05:00
}
}
2015-05-03 02:45:38 -04:00
listener_notify ( LISTENER_VOLUME ) ;
2016-02-08 16:17:10 -05:00
if ( cmd - > output_requests_pending > 0 )
2010-11-19 16:51:46 -05:00
return 1 ; /* async */
return 0 ;
}
2010-04-04 06:43:15 -04:00
static int
2010-09-13 12:43:11 -04:00
repeat_set ( struct player_command * cmd )
2010-04-04 06:43:15 -04:00
{
2015-05-03 02:45:38 -04:00
if ( cmd - > arg . mode = = repeat )
return 0 ;
2010-09-13 13:08:29 -04:00
switch ( cmd - > arg . mode )
2010-04-04 06:43:15 -04:00
{
case REPEAT_OFF :
case REPEAT_SONG :
case REPEAT_ALL :
2010-09-13 13:08:29 -04:00
repeat = cmd - > arg . mode ;
2010-04-04 06:43:15 -04:00
break ;
default :
2010-09-13 13:08:29 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Invalid repeat mode: %d \n " , cmd - > arg . mode ) ;
2010-04-04 06:43:15 -04:00
return - 1 ;
}
2015-05-03 02:45:38 -04:00
listener_notify ( LISTENER_OPTIONS ) ;
2010-04-04 06:43:15 -04:00
return 0 ;
}
static int
2010-09-13 12:43:11 -04:00
shuffle_set ( struct player_command * cmd )
2010-04-04 06:43:15 -04:00
{
2015-08-08 12:02:49 -04:00
uint32_t cur_id ;
2010-09-13 13:08:29 -04:00
switch ( cmd - > arg . 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 ;
2015-08-08 12:02:49 -04:00
queue_shuffle ( queue , cur_id ) ;
}
2010-04-04 06:43:15 -04:00
/* FALLTHROUGH*/
case 0 :
2010-09-13 13:08:29 -04:00
shuffle = cmd - > arg . intval ;
2010-04-04 06:43:15 -04:00
break ;
default :
2010-09-13 13:08:29 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Invalid shuffle mode: %d \n " , cmd - > arg . intval ) ;
2010-04-04 06:43:15 -04:00
return - 1 ;
}
2015-05-03 02:45:38 -04:00
listener_notify ( LISTENER_OPTIONS ) ;
2010-04-04 06:43:15 -04:00
return 0 ;
}
2015-08-08 12:02:49 -04:00
static int
playerqueue_get_bypos ( struct player_command * cmd )
2014-12-21 14:41:44 -05:00
{
int count ;
2015-10-03 02:24:13 -04:00
struct queue * qi ;
2014-12-21 14:41:44 -05:00
struct player_source * ps ;
2015-08-08 12:02:49 -04:00
int item_id ;
2014-12-21 14:41:44 -05:00
2015-08-08 12:02:49 -04:00
count = cmd - > arg . queue_get_param . count ;
2014-12-21 14:41:44 -05:00
2015-08-08 12:02:49 -04:00
ps = source_now_playing ( ) ;
2014-12-21 14:41:44 -05:00
2015-08-08 12:02:49 -04:00
item_id = 0 ;
if ( ps )
2015-08-01 05:43:51 -04:00
{
2015-10-03 03:01:26 -04:00
item_id = ps - > item_id ;
2014-12-21 14:41:44 -05:00
}
2015-10-03 02:24:13 -04:00
qi = queue_new_bypos ( queue , item_id , count , shuffle ) ;
2014-12-21 14:41:44 -05:00
2015-08-08 12:02:49 -04:00
cmd - > arg . queue_get_param . queue = qi ;
2015-08-01 05:43:51 -04:00
return 0 ;
}
static int
2015-08-08 12:02:49 -04:00
playerqueue_get_byindex ( struct player_command * cmd )
2015-08-01 05:43:51 -04:00
{
int pos ;
int count ;
2015-10-03 02:24:13 -04:00
struct queue * qi ;
2015-08-01 05:43:51 -04:00
pos = cmd - > arg . queue_get_param . pos ;
count = cmd - > arg . queue_get_param . count ;
2015-10-03 02:24:13 -04:00
qi = queue_new_byindex ( queue , pos , count , 0 ) ;
2015-08-08 12:02:49 -04:00
cmd - > arg . queue_get_param . queue = qi ;
2014-12-21 14:41:44 -05:00
return 0 ;
}
2010-04-04 06:43:15 -04:00
static int
2015-08-08 01:01:45 -04:00
playerqueue_add ( struct player_command * cmd )
2010-04-04 06:43:15 -04:00
{
2015-08-08 12:02:49 -04:00
struct queue_item * items ;
uint32_t cur_id ;
2010-04-04 06:43:15 -04:00
2015-08-08 12:02:49 -04:00
items = cmd - > arg . queue_add_param . items ;
2010-04-04 06:43:15 -04:00
2015-08-08 12:02:49 -04:00
queue_add ( queue , items ) ;
2010-04-04 06:43:15 -04:00
2015-08-08 12:02:49 -04:00
if ( shuffle )
2010-04-04 06:43:15 -04:00
{
2015-10-03 03:01:26 -04:00
cur_id = cur_streaming ? cur_streaming - > item_id : 0 ;
2015-08-08 12:02:49 -04:00
queue_shuffle ( queue , cur_id ) ;
2010-04-04 06:43:15 -04:00
}
2015-08-08 12:02:49 -04:00
//TODO [refactor] Unnecessary if, always set plid to 0 after adding items
2010-07-31 06:30:51 -04:00
if ( cur_plid ! = 0 )
cur_plid = 0 ;
2015-05-03 04:34:49 -04:00
cur_plversion + + ;
2010-07-31 06:30:51 -04:00
2015-05-03 02:45:38 -04:00
listener_notify ( LISTENER_PLAYLIST ) ;
2010-07-31 06:30:51 -04:00
2010-04-04 06:43:15 -04:00
return 0 ;
}
2014-04-19 03:12:58 -04:00
static int
2015-08-08 01:01:45 -04:00
playerqueue_add_next ( struct player_command * cmd )
2014-04-19 03:12:58 -04:00
{
2015-08-08 12:02:49 -04:00
struct queue_item * items ;
uint32_t cur_id ;
2014-04-19 03:12:58 -04:00
2015-08-08 12:02:49 -04:00
items = cmd - > arg . queue_add_param . items ;
2014-04-19 03:12:58 -04:00
2015-10-03 03:01:26 -04:00
cur_id = cur_streaming ? cur_streaming - > item_id : 0 ;
2015-08-08 12:02:49 -04:00
queue_add_after ( queue , items , cur_id ) ;
2014-04-19 03:12:58 -04:00
2014-07-13 03:59:37 -04:00
if ( shuffle )
2015-08-08 12:02:49 -04:00
queue_shuffle ( queue , cur_id ) ;
2014-07-13 03:59:37 -04:00
2015-08-08 12:02:49 -04:00
//TODO [refactor] Unnecessary if, always set plid to 0 after adding items
2014-04-19 03:12:58 -04:00
if ( cur_plid ! = 0 )
cur_plid = 0 ;
2015-05-03 04:34:49 -04:00
cur_plversion + + ;
2014-04-19 03:12:58 -04:00
2015-05-03 02:45:38 -04:00
listener_notify ( LISTENER_PLAYLIST ) ;
2014-04-19 03:12:58 -04:00
return 0 ;
}
static int
2015-08-08 12:02:49 -04:00
playerqueue_move_bypos ( struct player_command * cmd )
2014-04-19 02:09:32 -04:00
{
2015-08-08 12:02:49 -04:00
struct player_source * ps_playing ;
2014-04-19 02:09:32 -04:00
2015-10-30 02:11:34 -04:00
DPRINTF ( E_DBG , L_PLAYER , " Moving song from position %d to be the next song after %d \n " ,
cmd - > arg . queue_move_param . from_pos , cmd - > arg . queue_move_param . to_pos ) ;
2014-04-19 02:09:32 -04:00
2015-08-08 12:02:49 -04:00
ps_playing = source_now_playing ( ) ;
2015-08-01 04:16:40 -04:00
2015-08-08 12:02:49 -04:00
if ( ! ps_playing )
{
DPRINTF ( E_LOG , L_PLAYER , " Can't move item, no playing item found \n " ) ;
return - 1 ;
}
2015-08-01 04:16:40 -04:00
2015-10-30 02:11:34 -04:00
queue_move_bypos ( queue , ps_playing - > item_id , cmd - > arg . queue_move_param . from_pos , cmd - > arg . queue_move_param . to_pos , shuffle ) ;
cur_plversion + + ;
listener_notify ( LISTENER_PLAYLIST ) ;
return 0 ;
}
static int
playerqueue_move_byitemid ( struct player_command * cmd )
{
DPRINTF ( E_DBG , L_PLAYER , " Moving song with item-id %d to be the next song after index %d \n " ,
cmd - > arg . queue_move_param . item_id , cmd - > arg . queue_move_param . to_pos ) ;
queue_move_byitemid ( queue , cmd - > arg . queue_move_param . item_id , cmd - > arg . queue_move_param . to_pos , 0 ) ;
2015-08-01 04:16:40 -04:00
cur_plversion + + ;
listener_notify ( LISTENER_PLAYLIST ) ;
return 0 ;
}
static int
2015-08-08 12:02:49 -04:00
playerqueue_remove_bypos ( struct player_command * cmd )
2014-04-19 02:35:07 -04:00
{
2015-08-01 04:16:40 -04:00
int pos ;
2015-08-08 12:02:49 -04:00
struct player_source * ps_playing ;
2014-04-21 15:21:40 -04:00
2015-08-01 04:16:40 -04:00
pos = cmd - > arg . intval ;
if ( pos < 1 )
2014-12-21 14:41:44 -05:00
{
2015-08-01 04:16:40 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Can't remove item, invalid position %d \n " , pos ) ;
return - 1 ;
2014-12-21 14:41:44 -05:00
}
2015-08-08 12:02:49 -04:00
ps_playing = source_now_playing ( ) ;
2015-06-07 18:21:49 -04:00
2015-08-08 12:02:49 -04:00
if ( ! ps_playing )
2015-06-07 18:21:49 -04:00
{
2015-08-08 12:02:49 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Can't remove item at pos %d, no playing item found \n " , pos ) ;
2015-06-07 18:21:49 -04:00
return - 1 ;
}
2015-08-08 12:02:49 -04:00
DPRINTF ( E_DBG , L_PLAYER , " Removing item from position %d \n " , pos ) ;
2015-10-03 03:01:26 -04:00
queue_remove_bypos ( queue , ps_playing - > item_id , pos , shuffle ) ;
2015-06-07 18:21:49 -04:00
2015-11-21 04:08:54 -05:00
cur_plversion + + ;
listener_notify ( LISTENER_PLAYLIST ) ;
2015-08-08 12:02:49 -04:00
return 0 ;
2015-08-01 04:16:40 -04:00
}
2014-04-19 02:35:07 -04:00
2015-11-22 06:04:35 -05:00
static int
playerqueue_remove_byindex ( struct player_command * cmd )
{
int pos ;
int count ;
int i ;
pos = cmd - > arg . queue_remove_param . from_pos ;
count = cmd - > arg . queue_remove_param . count ;
DPRINTF ( E_DBG , L_PLAYER , " Removing %d items starting from position %d \n " , count , pos ) ;
for ( i = 0 ; i < count ; i + + )
queue_remove_byindex ( queue , pos , 0 ) ;
cur_plversion + + ;
listener_notify ( LISTENER_PLAYLIST ) ;
return 0 ;
}
2015-08-01 04:16:40 -04:00
static int
2015-08-08 12:02:49 -04:00
playerqueue_remove_byitemid ( struct player_command * cmd )
2015-08-01 04:16:40 -04:00
{
uint32_t id ;
2014-04-21 15:21:40 -04:00
2015-08-01 04:16:40 -04:00
id = cmd - > arg . id ;
if ( id < 1 )
{
DPRINTF ( E_LOG , L_PLAYER , " Can't remove item, invalid id %d \n " , id ) ;
return - 1 ;
}
2014-04-19 02:35:07 -04:00
2015-08-01 04:16:40 -04:00
DPRINTF ( E_DBG , L_PLAYER , " Removing item with id %d \n " , id ) ;
2015-08-08 12:02:49 -04:00
queue_remove_byitemid ( queue , id ) ;
2015-05-03 04:34:49 -04:00
2015-11-21 04:08:54 -05:00
cur_plversion + + ;
listener_notify ( LISTENER_PLAYLIST ) ;
2015-08-08 12:02:49 -04:00
return 0 ;
2014-04-19 02:35:07 -04:00
}
2014-05-29 03:45:04 -04:00
/*
2015-08-08 12:02:49 -04:00
* Removes all media items from the queue
2014-05-29 03:45:04 -04:00
*/
2010-04-04 06:43:15 -04:00
static int
2015-08-08 01:01:45 -04:00
playerqueue_clear ( struct player_command * cmd )
2010-04-04 06:43:15 -04:00
{
2015-08-08 12:02:49 -04:00
queue_clear ( queue ) ;
2010-04-04 06:43:15 -04:00
2010-07-31 06:30:51 -04:00
cur_plid = 0 ;
2015-05-03 04:34:49 -04:00
cur_plversion + + ;
2010-07-31 06:30:51 -04:00
2015-05-03 02:45:38 -04:00
listener_notify ( LISTENER_PLAYLIST ) ;
2010-07-31 06:30:51 -04:00
return 0 ;
}
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
*/
2014-05-17 08:06:50 -04:00
static int
2015-08-08 12:02:49 -04:00
playerqueue_clear_history ( struct player_command * cmd )
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
2015-05-03 04:34:49 -04:00
cur_plversion + + ;
2015-05-03 02:45:38 -04:00
listener_notify ( LISTENER_PLAYLIST ) ;
2014-05-17 08:06:50 -04:00
return 0 ;
}
2010-07-31 06:30:51 -04:00
static int
2015-08-08 01:01:45 -04:00
playerqueue_plid ( struct player_command * cmd )
2010-07-31 06:30:51 -04:00
{
2010-09-13 13:08:29 -04:00
cur_plid = cmd - > arg . id ;
2010-07-31 06:30:51 -04:00
2010-04-04 06:43:15 -04:00
return 0 ;
}
2010-09-13 12:43:11 -04:00
/* Command processing */
2010-04-04 06:43:15 -04:00
/* Thread: player */
static void
command_cb ( int fd , short what , void * arg )
{
2010-09-13 12:43:11 -04:00
struct player_command * cmd ;
2010-04-04 06:43:15 -04:00
int ret ;
2010-09-13 12:43:11 -04:00
ret = read ( cmd_pipe [ 0 ] , & cmd , sizeof ( cmd ) ) ;
if ( ret ! = sizeof ( cmd ) )
2010-04-04 06:43:15 -04:00
{
2010-09-13 12:43:11 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Could not read command! (read %d): %s \n " , ret , ( ret < 0 ) ? strerror ( errno ) : " -no error- " ) ;
2010-04-04 06:43:15 -04:00
goto readd ;
}
2010-11-19 13:26:29 -05:00
if ( cmd - > nonblock )
{
cmd - > func ( cmd ) ;
free ( cmd ) ;
goto readd ;
}
2010-09-13 12:43:11 -04:00
pthread_mutex_lock ( & cmd - > lck ) ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
cur_cmd = cmd ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
ret = cmd - > func ( cmd ) ;
2010-04-04 06:43:15 -04:00
if ( ret < = 0 )
{
2010-09-13 12:43:11 -04:00
cmd - > ret = ret ;
cur_cmd = NULL ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
pthread_cond_signal ( & cmd - > cond ) ;
pthread_mutex_unlock ( & cmd - > lck ) ;
2010-04-04 06:43:15 -04:00
}
2010-09-13 12:43:11 -04:00
else
{
/* Command is asynchronous, we don't want to process another command
* before we ' re done with this one . See command_async_end ( ) .
*/
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
return ;
}
2010-04-04 06:43:15 -04:00
readd :
2014-09-27 16:59:19 -04:00
event_add ( cmdev , NULL ) ;
2010-04-04 06:43:15 -04:00
}
2010-11-19 13:26:29 -05:00
/* Thread: httpd (DACP) - mDNS */
2010-04-04 06:43:15 -04:00
static int
2010-11-19 13:26:29 -05:00
send_command ( struct player_command * cmd )
2010-04-04 06:43:15 -04:00
{
int ret ;
2010-09-13 12:43:11 -04:00
if ( ! cmd - > func )
2010-04-04 06:43:15 -04:00
{
2010-09-13 12:43:11 -04:00
DPRINTF ( E_LOG , L_PLAYER , " BUG: cmd->func is NULL! \n " ) ;
2010-04-04 06:43:15 -04:00
return - 1 ;
}
2010-09-13 12:43:11 -04:00
ret = write ( cmd_pipe [ 1 ] , & cmd , sizeof ( cmd ) ) ;
if ( ret ! = sizeof ( cmd ) )
2010-04-04 06:43:15 -04:00
{
2010-09-13 12:43:11 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Could not send command: %s \n " , strerror ( errno ) ) ;
2010-04-04 06:43:15 -04:00
2010-11-19 13:26:29 -05:00
return - 1 ;
}
return 0 ;
}
/* Thread: mDNS */
static int
nonblock_command ( struct player_command * cmd )
{
int ret ;
ret = send_command ( cmd ) ;
if ( ret < 0 )
return - 1 ;
return 0 ;
}
/* Thread: httpd (DACP) */
static int
sync_command ( struct player_command * cmd )
{
int ret ;
pthread_mutex_lock ( & cmd - > lck ) ;
ret = send_command ( cmd ) ;
if ( ret < 0 )
{
2010-09-13 12:43:11 -04:00
pthread_mutex_unlock ( & cmd - > lck ) ;
2010-11-19 13:26:29 -05:00
2010-04-04 06:43:15 -04:00
return - 1 ;
}
2010-09-13 12:43:11 -04:00
pthread_cond_wait ( & cmd - > cond , & cmd - > lck ) ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
pthread_mutex_unlock ( & cmd - > lck ) ;
ret = cmd - > ret ;
2010-04-04 06:43:15 -04:00
return ret ;
}
/* Player API executed in the httpd (DACP) thread */
int
player_get_status ( struct player_status * status )
{
2010-09-13 12:43:11 -04:00
struct player_command cmd ;
2010-04-04 06:43:15 -04:00
int ret ;
2010-09-13 12:43:11 -04:00
command_init ( & cmd ) ;
2010-04-04 06:43:15 -04:00
cmd . func = get_status ;
cmd . func_bh = NULL ;
2010-09-13 13:08:29 -04:00
cmd . arg . status = status ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
ret = sync_command ( & cmd ) ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
command_deinit ( & cmd ) ;
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 )
{
2010-09-13 12:43:11 -04:00
struct player_command cmd ;
2010-04-04 06:43:15 -04:00
int ret ;
2010-09-13 12:43:11 -04:00
command_init ( & cmd ) ;
2010-04-04 06:43:15 -04:00
cmd . func = now_playing ;
cmd . func_bh = NULL ;
2010-09-13 13:08:29 -04:00
cmd . arg . id_ptr = id ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
ret = sync_command ( & cmd ) ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
command_deinit ( & cmd ) ;
2010-04-04 06:43:15 -04:00
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
{
struct player_command cmd ;
int ret ;
command_init ( & cmd ) ;
cmd . func = artwork_url_get ;
cmd . func_bh = NULL ;
2015-04-09 16:22:42 -04:00
cmd . arg . icy . id = id ;
2015-03-14 16:42:53 -04:00
if ( pthread_self ( ) ! = tid_player )
ret = sync_command ( & cmd ) ;
else
ret = artwork_url_get ( & cmd ) ;
command_deinit ( & cmd ) ;
2015-04-09 16:22:42 -04:00
if ( ret < 0 )
return NULL ;
else
return cmd . arg . 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
2015-10-03 02:49:04 -04:00
player_playback_start ( uint32_t * id )
2014-12-21 14:41:44 -05:00
{
struct player_command cmd ;
int ret ;
command_init ( & cmd ) ;
cmd . func = playback_start ;
cmd . func_bh = playback_start_bh ;
2015-10-03 02:49:04 -04:00
cmd . arg . playback_start_param . id_ptr = id ;
2014-12-21 14:41:44 -05:00
ret = sync_command ( & cmd ) ;
command_deinit ( & cmd ) ;
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
2015-10-03 02:49:04 -04:00
player_playback_start_byindex ( int index , uint32_t * id )
2010-04-04 06:43:15 -04:00
{
2010-09-13 12:43:11 -04:00
struct player_command cmd ;
2010-04-04 06:43:15 -04:00
int ret ;
2010-09-13 12:43:11 -04:00
command_init ( & cmd ) ;
2010-04-04 06:43:15 -04:00
2015-08-08 12:02:49 -04:00
cmd . func = playback_start_byindex ;
2010-04-04 06:43:15 -04:00
cmd . func_bh = playback_start_bh ;
2015-08-08 12:02:49 -04:00
cmd . arg . playback_start_param . pos = index ;
2015-10-03 02:49:04 -04:00
cmd . arg . playback_start_param . id_ptr = id ;
2014-12-21 14:41:44 -05:00
ret = sync_command ( & cmd ) ;
command_deinit ( & cmd ) ;
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 position in the UpNext - queue .
* The UpNext - queue consists of all items of the play - queue ( shuffle off ) or shuffle - queue
* ( shuffle on ) after the current playing item ( starting with position 0 ) .
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-08-08 12:02:49 -04:00
* @ param pos the position in the UpNext - queue ( zero - based )
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
*/
2014-12-21 14:41:44 -05:00
int
2015-10-03 02:49:04 -04:00
player_playback_start_bypos ( int pos , uint32_t * id )
2014-12-21 14:41:44 -05:00
{
struct player_command cmd ;
int ret ;
2010-04-04 06:43:15 -04:00
2014-12-21 14:41:44 -05:00
command_init ( & cmd ) ;
2015-08-08 12:02:49 -04:00
cmd . func = playback_start_bypos ;
cmd . func_bh = playback_start_bh ;
cmd . arg . playback_start_param . pos = pos ;
2015-10-03 02:49:04 -04:00
cmd . arg . playback_start_param . id_ptr = id ;
2015-08-08 12:02:49 -04:00
ret = sync_command ( & cmd ) ;
command_deinit ( & cmd ) ;
return ret ;
}
/*
* Starts playback with the media item with the given ( queueitem ) item - id in queue
*
* 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 dbmfi - id .
*
2015-10-03 02:49:04 -04:00
* @ param item_id The queue - item - id
* @ param * id if not NULL , will be set to the playing item dbmfi - id
2015-08-08 12:02:49 -04:00
* @ return 0 if successful , - 1 if an error occurred
*/
int
2015-10-03 02:49:04 -04:00
player_playback_start_byitemid ( uint32_t item_id , uint32_t * id )
2015-08-08 12:02:49 -04:00
{
struct player_command cmd ;
int ret ;
command_init ( & cmd ) ;
cmd . func = playback_start_byitemid ;
2014-12-21 14:41:44 -05:00
cmd . func_bh = playback_start_bh ;
2015-10-03 02:49:04 -04:00
cmd . arg . playback_start_param . id = item_id ;
cmd . arg . playback_start_param . id_ptr = id ;
2010-09-13 12:43:11 -04:00
ret = sync_command ( & cmd ) ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
command_deinit ( & cmd ) ;
2010-04-04 06:43:15 -04:00
return ret ;
}
int
player_playback_stop ( void )
{
2010-09-13 12:43:11 -04:00
struct player_command cmd ;
2010-04-04 06:43:15 -04:00
int ret ;
2010-09-13 12:43:11 -04:00
command_init ( & cmd ) ;
2010-04-04 06:43:15 -04:00
cmd . func = playback_stop ;
2010-09-13 13:08:29 -04:00
cmd . arg . noarg = NULL ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
ret = sync_command ( & cmd ) ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
command_deinit ( & cmd ) ;
2010-04-04 06:43:15 -04:00
return ret ;
}
int
player_playback_pause ( void )
{
2010-09-13 12:43:11 -04:00
struct player_command cmd ;
2010-04-04 06:43:15 -04:00
int ret ;
2010-09-13 12:43:11 -04:00
command_init ( & cmd ) ;
2010-04-04 06:43:15 -04:00
cmd . func = playback_pause ;
cmd . func_bh = playback_pause_bh ;
2010-09-13 13:08:29 -04:00
cmd . arg . noarg = NULL ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
ret = sync_command ( & cmd ) ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
command_deinit ( & cmd ) ;
2010-04-04 06:43:15 -04:00
return ret ;
}
int
player_playback_seek ( int ms )
{
2010-09-13 12:43:11 -04:00
struct player_command cmd ;
2010-04-04 06:43:15 -04:00
int ret ;
2010-09-13 12:43:11 -04:00
command_init ( & cmd ) ;
2010-04-04 06:43:15 -04:00
cmd . func = playback_pause ;
cmd . func_bh = playback_seek_bh ;
2010-09-13 13:08:29 -04:00
cmd . arg . intval = ms ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
ret = sync_command ( & cmd ) ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
command_deinit ( & cmd ) ;
2010-04-04 06:43:15 -04:00
return ret ;
}
int
player_playback_next ( void )
{
2010-09-13 12:43:11 -04:00
struct player_command cmd ;
2010-04-04 06:43:15 -04:00
int ret ;
2010-10-07 14:47:57 -04:00
command_init ( & cmd ) ;
2010-04-04 06:43:15 -04:00
cmd . func = playback_pause ;
cmd . func_bh = playback_next_bh ;
2010-09-13 13:08:29 -04:00
cmd . arg . noarg = NULL ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
ret = sync_command ( & cmd ) ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
command_deinit ( & cmd ) ;
2010-04-04 06:43:15 -04:00
return ret ;
}
int
player_playback_prev ( void )
{
2010-09-13 12:43:11 -04:00
struct player_command cmd ;
2010-04-04 06:43:15 -04:00
int ret ;
2010-09-13 12:43:11 -04:00
command_init ( & cmd ) ;
2010-04-04 06:43:15 -04:00
cmd . func = playback_pause ;
cmd . func_bh = playback_prev_bh ;
2010-09-13 13:08:29 -04:00
cmd . arg . noarg = NULL ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
ret = sync_command ( & cmd ) ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
command_deinit ( & cmd ) ;
2010-04-04 06:43:15 -04:00
return ret ;
}
2015-10-09 17:58:27 -04:00
void
player_streaming_start ( player_streaming_cb cb )
{
streaming_write = cb ;
streaming_selected = 1 ;
}
void
player_streaming_stop ( void )
{
streaming_selected = 0 ;
}
2010-04-04 06:43:15 -04:00
void
player_speaker_enumerate ( spk_enum_cb cb , void * arg )
{
2010-12-02 13:51:08 -05:00
struct player_command cmd ;
struct spk_enum spk_enum ;
2010-04-04 06:43:15 -04:00
2010-12-02 13:51:08 -05:00
command_init ( & cmd ) ;
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
2010-12-02 13:51:08 -05:00
cmd . func = speaker_enumerate ;
cmd . func_bh = NULL ;
cmd . arg . spk_enum = & spk_enum ;
2010-04-04 06:43:15 -04:00
2010-12-02 13:51:08 -05:00
sync_command ( & cmd ) ;
2010-04-04 06:43:15 -04:00
2010-12-02 13:51:08 -05:00
command_deinit ( & cmd ) ;
2010-04-04 06:43:15 -04:00
}
int
player_speaker_set ( uint64_t * ids )
{
2010-09-13 12:43:11 -04:00
struct player_command cmd ;
2010-04-04 06:43:15 -04:00
int ret ;
2010-09-13 12:43:11 -04:00
command_init ( & cmd ) ;
2010-04-04 06:43:15 -04:00
cmd . func = speaker_set ;
cmd . func_bh = NULL ;
2016-01-23 19:14:07 -05:00
cmd . arg . device_ids = ids ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
ret = sync_command ( & cmd ) ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
command_deinit ( & cmd ) ;
2010-04-04 06:43:15 -04:00
return ret ;
}
int
player_volume_set ( int vol )
{
2010-09-13 12:43:11 -04:00
struct player_command cmd ;
2010-04-04 06:43:15 -04:00
int ret ;
2010-09-13 12:43:11 -04:00
command_init ( & cmd ) ;
2010-04-04 06:43:15 -04:00
cmd . func = volume_set ;
cmd . func_bh = NULL ;
2010-09-13 13:08:29 -04:00
cmd . arg . intval = vol ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
ret = sync_command ( & cmd ) ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
command_deinit ( & cmd ) ;
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 )
{
struct player_command cmd ;
int ret ;
command_init ( & cmd ) ;
cmd . func = volume_setrel_speaker ;
cmd . func_bh = NULL ;
cmd . arg . vol_param . spk_id = id ;
cmd . arg . vol_param . volume = relvol ;
ret = sync_command ( & cmd ) ;
command_deinit ( & cmd ) ;
return ret ;
}
int
player_volume_setabs_speaker ( uint64_t id , int vol )
{
struct player_command cmd ;
int ret ;
command_init ( & cmd ) ;
cmd . func = volume_setabs_speaker ;
cmd . func_bh = NULL ;
cmd . arg . vol_param . spk_id = id ;
cmd . arg . vol_param . volume = vol ;
ret = sync_command ( & cmd ) ;
command_deinit ( & cmd ) ;
return ret ;
}
2010-04-04 06:43:15 -04:00
int
player_repeat_set ( enum repeat_mode mode )
{
2010-09-13 12:43:11 -04:00
struct player_command cmd ;
2010-04-04 06:43:15 -04:00
int ret ;
2010-09-13 12:43:11 -04:00
command_init ( & cmd ) ;
2010-04-04 06:43:15 -04:00
cmd . func = repeat_set ;
cmd . func_bh = NULL ;
2010-09-13 13:08:29 -04:00
cmd . arg . mode = mode ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
ret = sync_command ( & cmd ) ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
command_deinit ( & cmd ) ;
2010-04-04 06:43:15 -04:00
return ret ;
}
int
player_shuffle_set ( int enable )
{
2010-09-13 12:43:11 -04:00
struct player_command cmd ;
2010-04-04 06:43:15 -04:00
int ret ;
2010-09-13 12:43:11 -04:00
command_init ( & cmd ) ;
2010-04-04 06:43:15 -04:00
cmd . func = shuffle_set ;
cmd . func_bh = NULL ;
2010-09-13 13:08:29 -04:00
cmd . arg . intval = enable ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
ret = sync_command ( & cmd ) ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
command_deinit ( & cmd ) ;
2010-04-04 06:43:15 -04:00
return ret ;
}
2015-02-14 16:36:52 -05:00
/*
2015-08-08 12:02:49 -04:00
* Returns the queue info for max " count " media items in the UpNext - queue
2015-02-14 16:36:52 -05:00
*
2015-08-08 12:02:49 -04:00
* The UpNext - queue consists of all items of the play - queue ( shuffle off ) or shuffle - queue
* ( shuffle on ) after the current playing item ( starting with position 0 ) .
2015-02-14 16:36:52 -05:00
*
2015-08-08 12:02:49 -04:00
* @ param count max number of media items to return
* @ return queue info
2015-02-14 16:36:52 -05:00
*/
2015-10-03 02:24:13 -04:00
struct queue *
2015-08-08 12:02:49 -04:00
player_queue_get_bypos ( int count )
2014-12-21 14:41:44 -05:00
{
struct player_command cmd ;
int ret ;
command_init ( & cmd ) ;
2015-08-08 12:02:49 -04:00
cmd . func = playerqueue_get_bypos ;
2014-12-21 14:41:44 -05:00
cmd . func_bh = NULL ;
2015-08-01 05:43:51 -04:00
cmd . arg . queue_get_param . pos = - 1 ;
cmd . arg . queue_get_param . count = count ;
cmd . arg . queue_get_param . queue = NULL ;
ret = sync_command ( & cmd ) ;
command_deinit ( & cmd ) ;
if ( ret ! = 0 )
return NULL ;
return cmd . arg . queue_get_param . queue ;
}
2015-08-08 12:02:49 -04:00
/*
* Returns the queue info for max " count " media items starting with the item at the given
* index in the play - queue
*
* @ param index Index of the play - queue for the first item
* @ param count max number of media items to return
* @ return queue info
*/
2015-10-03 02:24:13 -04:00
struct queue *
2015-08-08 12:02:49 -04:00
player_queue_get_byindex ( int index , int count )
2015-08-01 05:43:51 -04:00
{
struct player_command cmd ;
int ret ;
command_init ( & cmd ) ;
2015-08-08 12:02:49 -04:00
cmd . func = playerqueue_get_byindex ;
2015-08-01 05:43:51 -04:00
cmd . func_bh = NULL ;
2015-08-08 12:02:49 -04:00
cmd . arg . queue_get_param . pos = index ;
2015-08-01 05:43:51 -04:00
cmd . arg . queue_get_param . count = count ;
cmd . arg . queue_get_param . queue = NULL ;
2014-12-21 14:41:44 -05:00
ret = sync_command ( & cmd ) ;
command_deinit ( & cmd ) ;
if ( ret ! = 0 )
return NULL ;
2015-08-01 05:43:51 -04:00
return cmd . arg . queue_get_param . queue ;
2014-12-21 14:41:44 -05:00
}
2015-08-08 12:02:49 -04:00
/*
* Appends the given media items to the queue
*/
2010-04-04 06:43:15 -04:00
int
2015-08-08 12:02:49 -04:00
player_queue_add ( struct queue_item * items )
2010-04-04 06:43:15 -04:00
{
2010-09-13 12:43:11 -04:00
struct player_command cmd ;
2010-04-04 06:43:15 -04:00
int ret ;
2010-09-13 12:43:11 -04:00
command_init ( & cmd ) ;
2010-04-04 06:43:15 -04:00
2015-08-08 01:01:45 -04:00
cmd . func = playerqueue_add ;
2010-04-04 06:43:15 -04:00
cmd . func_bh = NULL ;
2015-08-08 12:02:49 -04:00
cmd . arg . queue_add_param . items = items ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
ret = sync_command ( & cmd ) ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
command_deinit ( & cmd ) ;
2010-04-04 06:43:15 -04:00
return ret ;
}
2015-08-08 12:02:49 -04:00
/*
* Adds the given media items directly after the current playing / streaming media item
*/
2014-04-19 03:12:58 -04:00
int
2015-08-08 12:02:49 -04:00
player_queue_add_next ( struct queue_item * items )
2014-04-19 03:12:58 -04:00
{
struct player_command cmd ;
int ret ;
command_init ( & cmd ) ;
2015-08-08 01:01:45 -04:00
cmd . func = playerqueue_add_next ;
2014-04-19 03:12:58 -04:00
cmd . func_bh = NULL ;
2015-08-08 12:02:49 -04:00
cmd . arg . queue_add_param . items = items ;
2014-04-19 03:12:58 -04:00
ret = sync_command ( & cmd ) ;
command_deinit ( & cmd ) ;
return ret ;
}
2015-08-08 12:02:49 -04:00
/*
* Moves the media item at ' pos_from ' to ' pos_to ' in the UpNext - queue .
*
* The UpNext - queue consists of all items of the play - queue ( shuffle off ) or shuffle - queue
* ( shuffle on ) after the current playing item ( starting with position 0 ) .
*/
2014-04-19 02:09:32 -04:00
int
2015-08-08 12:02:49 -04:00
player_queue_move_bypos ( int pos_from , int pos_to )
2014-04-19 02:09:32 -04:00
{
struct player_command cmd ;
int ret ;
command_init ( & cmd ) ;
2015-08-08 12:02:49 -04:00
cmd . func = playerqueue_move_bypos ;
2014-04-19 02:09:32 -04:00
cmd . func_bh = NULL ;
2015-10-30 02:11:34 -04:00
cmd . arg . queue_move_param . from_pos = pos_from ;
cmd . arg . queue_move_param . to_pos = pos_to ;
ret = sync_command ( & cmd ) ;
command_deinit ( & cmd ) ;
return ret ;
}
int
player_queue_move_byitemid ( uint32_t item_id , int pos_to )
{
struct player_command cmd ;
int ret ;
command_init ( & cmd ) ;
cmd . func = playerqueue_move_byitemid ;
cmd . func_bh = NULL ;
cmd . arg . queue_move_param . item_id = item_id ;
cmd . arg . queue_move_param . to_pos = pos_to ;
2014-04-19 02:09:32 -04:00
ret = sync_command ( & cmd ) ;
command_deinit ( & cmd ) ;
return ret ;
}
2015-08-01 04:16:40 -04:00
/*
2015-08-08 12:02:49 -04:00
* Removes the media item at the given position from the UpNext - queue
2015-08-01 04:16:40 -04:00
*
2015-08-08 12:02:49 -04:00
* The UpNext - queue consists of all items of the play - queue ( shuffle off ) or shuffle - queue
* ( shuffle on ) after the current playing item ( starting with position 0 ) .
*
* @ param pos Position in the UpNext - queue ( 0 - based )
2015-08-01 04:16:40 -04:00
* @ return 0 on success , - 1 on failure
*/
2014-12-21 14:41:44 -05:00
int
2015-08-08 12:02:49 -04:00
player_queue_remove_bypos ( int pos )
2014-12-21 14:41:44 -05:00
{
struct player_command cmd ;
int ret ;
command_init ( & cmd ) ;
2015-08-08 12:02:49 -04:00
cmd . func = playerqueue_remove_bypos ;
2014-12-21 14:41:44 -05:00
cmd . func_bh = NULL ;
2015-08-01 04:16:40 -04:00
cmd . arg . intval = pos ;
2014-12-21 14:41:44 -05:00
ret = sync_command ( & cmd ) ;
command_deinit ( & cmd ) ;
return ret ;
}
2015-11-22 06:04:35 -05:00
/*
* Removes the media item at the given position from the UpNext - queue
*
* The UpNext - queue consists of all items of the play - queue ( shuffle off ) or shuffle - queue
* ( shuffle on ) after the current playing item ( starting with position 0 ) .
*
* @ param pos Position in the UpNext - queue ( 0 - based )
* @ return 0 on success , - 1 on failure
*/
int
player_queue_remove_byindex ( int pos , int count )
{
struct player_command cmd ;
int ret ;
command_init ( & cmd ) ;
cmd . func = playerqueue_remove_byindex ;
cmd . func_bh = NULL ;
cmd . arg . queue_remove_param . from_pos = pos ;
cmd . arg . queue_remove_param . count = count ;
ret = sync_command ( & cmd ) ;
command_deinit ( & cmd ) ;
return ret ;
}
2015-08-01 04:16:40 -04:00
/*
2015-08-08 12:02:49 -04:00
* Removes the item with the given ( queueitem ) item id from the queue
2015-08-01 04:16:40 -04:00
*
* @ param id Id of the queue item to remove
* @ return 0 on success , - 1 on failure
*/
2014-12-21 14:41:44 -05:00
int
2015-08-08 12:02:49 -04:00
player_queue_remove_byitemid ( uint32_t id )
2014-04-19 02:35:07 -04:00
{
struct player_command cmd ;
int ret ;
command_init ( & cmd ) ;
2015-08-08 12:02:49 -04:00
cmd . func = playerqueue_remove_byitemid ;
2014-04-19 02:35:07 -04:00
cmd . func_bh = NULL ;
2015-08-01 04:16:40 -04:00
cmd . arg . id = id ;
2014-04-19 02:35:07 -04:00
ret = sync_command ( & cmd ) ;
command_deinit ( & cmd ) ;
return ret ;
}
2010-04-04 06:43:15 -04:00
void
player_queue_clear ( void )
{
2010-09-13 12:43:11 -04:00
struct player_command cmd ;
command_init ( & cmd ) ;
2010-04-04 06:43:15 -04:00
2015-08-08 01:01:45 -04:00
cmd . func = playerqueue_clear ;
2010-04-04 06:43:15 -04:00
cmd . func_bh = NULL ;
2010-09-13 13:08:29 -04:00
cmd . arg . noarg = NULL ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
sync_command ( & cmd ) ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
command_deinit ( & cmd ) ;
2010-04-04 06:43:15 -04:00
}
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
{
struct player_command cmd ;
command_init ( & cmd ) ;
2015-08-08 12:02:49 -04:00
cmd . func = playerqueue_clear_history ;
2014-05-17 08:06:50 -04:00
cmd . func_bh = NULL ;
sync_command ( & cmd ) ;
command_deinit ( & cmd ) ;
}
2010-07-31 06:30:51 -04:00
void
player_queue_plid ( uint32_t plid )
{
2010-09-13 12:43:11 -04:00
struct player_command cmd ;
command_init ( & cmd ) ;
2010-07-31 06:30:51 -04:00
2015-08-08 01:01:45 -04:00
cmd . func = playerqueue_plid ;
2010-07-31 06:30:51 -04:00
cmd . func_bh = NULL ;
2010-09-13 13:08:29 -04:00
cmd . arg . id = plid ;
2010-07-31 06:30:51 -04:00
2010-09-13 12:43:11 -04:00
sync_command ( & cmd ) ;
2010-07-31 06:30:51 -04:00
2010-09-13 12:43:11 -04:00
command_deinit ( & cmd ) ;
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
{
2010-11-19 14:20:37 -05:00
struct player_command * cmd ;
2010-11-02 12:55:26 -04:00
int ret ;
2010-09-30 13:24:34 -04:00
2016-01-23 19:14:07 -05:00
cmd = calloc ( 1 , sizeof ( struct player_command ) ) ;
2010-11-19 14:20:37 -05:00
if ( ! cmd )
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
}
2010-11-19 14:20:37 -05:00
cmd - > nonblock = 1 ;
cmd - > func = device_add ;
2016-01-23 19:14:07 -05:00
cmd - > arg . device = device ;
2010-11-19 14:20:37 -05:00
ret = nonblock_command ( cmd ) ;
if ( ret < 0 )
2010-09-30 13:24:34 -04:00
{
2010-11-19 14:20:37 -05:00
free ( cmd ) ;
2016-01-23 19:14:07 -05:00
return - 1 ;
2010-09-30 13:24:34 -04:00
}
2016-01-23 19:14:07 -05:00
return 0 ;
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
{
struct player_command * cmd ;
int ret ;
2010-09-30 13:24:34 -04:00
2016-01-23 19:14:07 -05:00
cmd = calloc ( 1 , sizeof ( struct player_command ) ) ;
2010-11-19 14:20:37 -05:00
if ( ! cmd )
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
}
2010-11-19 14:20:37 -05:00
cmd - > nonblock = 1 ;
cmd - > func = device_remove_family ;
2016-01-23 19:14:07 -05:00
cmd - > arg . device = device ;
2010-11-19 14:20:37 -05:00
ret = nonblock_command ( cmd ) ;
if ( ret < 0 )
{
free ( cmd ) ;
2016-01-23 19:14:07 -05:00
return - 1 ;
2010-11-19 14:20:37 -05:00
}
2016-01-23 19:14:07 -05:00
return 0 ;
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
{
2015-04-09 15:04:35 -04:00
struct player_command cmd ;
2015-04-07 17:35:56 -04:00
2015-04-09 15:04:35 -04:00
command_init ( & cmd ) ;
2015-04-07 17:35:56 -04:00
2015-04-09 15:04:35 -04:00
cmd . func = metadata_send ;
cmd . func_bh = NULL ;
cmd . arg . pmd = pmd ;
2015-04-07 17:35:56 -04:00
2015-04-09 15:04:35 -04:00
sync_command ( & cmd ) ;
2015-04-07 17:35:56 -04:00
2015-04-09 15:04:35 -04:00
command_deinit ( & cmd ) ;
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-02-08 16:17:10 -05:00
ret = db_speaker_save ( 0 , laudio_selected , laudio_volume , " Local audio " ) ;
2010-11-19 14:48:33 -05:00
if ( ret < 0 )
DPRINTF ( E_LOG , L_PLAYER , " Could not save state for local audio \n " ) ;
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: player */
static void
exit_cb ( int fd , short what , void * arg )
{
event_base_loopbreak ( evbase_player ) ;
player_exit = 1 ;
}
/* 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 ;
2010-11-19 14:31:41 -05:00
dev_autoselect = 1 ;
2010-04-04 06:43:15 -04:00
dev_list = NULL ;
2010-11-19 16:51:46 -05:00
master_volume = - 1 ;
2010-11-21 04:05:41 -05:00
laudio_selected = 0 ;
2010-04-04 06:43:15 -04:00
laudio_status = LAUDIO_CLOSED ;
2016-01-23 19:14:07 -05:00
output_sessions = 0 ;
2010-04-04 06:43:15 -04:00
2010-09-13 12:43:11 -04:00
cur_cmd = NULL ;
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 ;
2015-08-08 12:02:49 -04:00
queue = queue_new ( ) ;
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 ;
2010-11-19 15:22:55 -05:00
ret = db_speaker_get ( 0 , & laudio_selected , & laudio_volume ) ;
2010-04-04 06:43:15 -04:00
if ( ret < 0 )
2015-09-27 09:24:04 -04:00
laudio_volume = PLAYER_DEFAULT_VOLUME ;
2010-11-19 14:48:33 -05:00
else if ( laudio_selected )
2016-02-11 02:29:29 -05:00
speaker_select_laudio ( ) ; // Run the select helper
2010-04-04 06:43:15 -04:00
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
}
ret = pipe2 ( exit_pipe , O_CLOEXEC ) ;
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Could not create pipe: %s \n " , strerror ( errno ) ) ;
goto exit_fail ;
}
ret = pipe2 ( cmd_pipe , O_CLOEXEC ) ;
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Could not create command pipe: %s \n " , strerror ( errno ) ) ;
goto cmd_fail ;
}
evbase_player = event_base_new ( ) ;
if ( ! evbase_player )
{
DPRINTF ( E_LOG , L_PLAYER , " Could not create an event base \n " ) ;
goto evbase_fail ;
}
2014-09-27 16:59:19 -04:00
exitev = event_new ( evbase_player , exit_pipe [ 0 ] , EV_READ , exit_cb , NULL ) ;
if ( ! exitev )
{
DPRINTF ( E_LOG , L_PLAYER , " Could not create exit event \n " ) ;
goto evnew_fail ;
}
cmdev = event_new ( evbase_player , cmd_pipe [ 0 ] , EV_READ , command_cb , NULL ) ;
if ( ! cmdev )
{
DPRINTF ( E_LOG , L_PLAYER , " Could not create cmd event \n " ) ;
goto evnew_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 ( exitev , NULL ) ;
event_add ( cmdev , NULL ) ;
event_add ( pb_timer_ev , NULL ) ;
2010-04-04 06:43:15 -04:00
ret = laudio_init ( player_laudio_status_cb ) ;
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Local audio init failed \n " ) ;
goto laudio_fail ;
}
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 ;
}
return 0 ;
thread_fail :
2016-01-23 19:14:07 -05:00
outputs_deinit ( ) ;
outputs_fail :
2010-04-04 06:43:15 -04:00
laudio_deinit ( ) ;
laudio_fail :
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 :
close ( cmd_pipe [ 0 ] ) ;
close ( cmd_pipe [ 1 ] ) ;
cmd_fail :
close ( exit_pipe [ 0 ] ) ;
close ( exit_pipe [ 1 ] ) ;
exit_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 ;
int dummy = 42 ;
ret = write ( exit_pipe [ 1 ] , & dummy , sizeof ( dummy ) ) ;
if ( ret ! = sizeof ( dummy ) )
{
2014-09-27 16:59:19 -04:00
DPRINTF ( E_LOG , L_PLAYER , " Could not write to exit pipe: %s \n " , strerror ( errno ) ) ;
2010-04-04 06:43:15 -04:00
return ;
}
ret = pthread_join ( tid_player , NULL ) ;
if ( ret ! = 0 )
{
DPRINTF ( E_LOG , L_PLAYER , " Could not join HTTPd thread: %s \n " , strerror ( errno ) ) ;
return ;
}
2015-08-08 12:02:49 -04:00
queue_free ( queue ) ;
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 ) ;
laudio_deinit ( ) ;
2016-01-23 19:14:07 -05:00
outputs_deinit ( ) ;
2010-04-04 06:43:15 -04:00
close ( exit_pipe [ 0 ] ) ;
close ( exit_pipe [ 1 ] ) ;
close ( cmd_pipe [ 0 ] ) ;
close ( cmd_pipe [ 1 ] ) ;
cmd_pipe [ 0 ] = - 1 ;
cmd_pipe [ 1 ] = - 1 ;
event_base_free ( evbase_player ) ;
}