8181#include "mass.h"
8282#include "wrappers.h"
8383
84- #define MASS_UPDATE_INTERVAL_SEC 1 // every second
85- #define MASS_MS_TILL_EXIT 5000 // exit after 5 seconds of pause
86- #define MASS_METADATA_KEYVAL_SEP "=" // Key-value separator in metadata
84+ #define MASS_UPDATE_INTERVAL_SEC 1 // every second
85+ #define MASS_METADATA_KEYVAL_SEP "=" // Key-value separator in metadata
8786#define MASS_METADATA_PROGRESS_KEY "PROGRESS"
8887#define MASS_METADATA_VOLUME_KEY "VOLUME"
8988#define MASS_METADATA_ARTWORK_KEY "ARTWORK"
9089#define MASS_METADATA_ALBUM_KEY "ALBUM"
9190#define MASS_METADATA_TITLE_KEY "TITLE"
9291#define MASS_METADATA_ARTIST_KEY "ARTIST"
93- #define MASS_METADATA_DURATION_KEY "DURATION"
94- #define MASS_METADATA_ACTION_KEY "ACTION"
92+ #define MASS_METADATA_DURATION_KEY "DURATION"
93+ #define MASS_METADATA_ACTION_KEY "ACTION"
94+ #define MASS_METADATA_PIN_KEY "PIN"
9595
9696/* from cliap2.c */
9797extern mass_named_pipes_t mass_named_pipes ;
@@ -137,6 +137,7 @@ enum pipe_metadata_msg
137137 PIPE_METADATA_MSG_STOP = (1 << 6 ),
138138 PIPE_METADATA_MSG_PAUSE = (1 << 7 ),
139139 PIPE_METADATA_MSG_PLAY = (1 << 8 ),
140+ PIPE_METADATA_MSG_PIN = (1 << 9 ),
140141};
141142
142143struct pipe
@@ -164,6 +165,8 @@ struct pipe_metadata_prepared
164165 char pict_tmpfile_path [sizeof (PIPE_TMPFILE_TEMPLATE )];
165166 // Volume
166167 int volume ;
168+ // PIN
169+ char * pin ; // 4 digit PIN
167170 // Mutex to share the prepared metadata
168171 pthread_mutex_t lock ;
169172};
@@ -835,7 +838,6 @@ void extract_key_value(const char *input_string, char **key, char **value) {
835838}
836839
837840// Parse one metadata item from Music Assistant
838- // TODO: @bradkeifer - check memory management to ensure key and value are freed properly
839841static int
840842parse_mass_item (enum pipe_metadata_msg * out_msg , struct pipe_metadata_prepared * prepared , const char * item )
841843{
@@ -924,6 +926,21 @@ parse_mass_item(enum pipe_metadata_msg *out_msg, struct pipe_metadata_prepared *
924926 free (value );
925927 DPRINTF (E_DBG , L_PLAYER , "%s:Parsed Music Assistant volume: %d\n" , __func__ , prepared -> volume );
926928 }
929+ else if (!strncmp (key ,MASS_METADATA_PIN_KEY , strlen (MASS_METADATA_PIN_KEY ))) {
930+ message = PIPE_METADATA_MSG_PIN ;
931+ uint32_t pin ;
932+ ret = safe_atou32 (value , & pin );
933+ if (ret < 0 || ret > 9999 ) { // PIN's limited to 4 digits
934+ DPRINTF (E_LOG , L_PLAYER , "%s:Invalid PIN value in Music Assistant metadata: '%s'\n" , __func__ , value );
935+ free (key );
936+ free (value );
937+ return -1 ;
938+ }
939+ asprintf (& prepared -> pin , "%0.4u" , pin );
940+ free (key );
941+ free (value );
942+ DPRINTF (E_DBG , L_PLAYER , "%s:Parsed Music Assistant PIN: %0.4s\n" , __func__ , prepared -> pin );
943+ }
927944 else if (!strncmp (key ,MASS_METADATA_ACTION_KEY , strlen (MASS_METADATA_ACTION_KEY ))) {
928945 if (strncmp (value , "SENDMETA" , strlen ("SENDMETA" )) == 0 ) {
929946 message = PIPE_METADATA_MSG_METADATA ;
@@ -962,75 +979,6 @@ parse_mass_item(enum pipe_metadata_msg *out_msg, struct pipe_metadata_prepared *
962979 return 0 ;
963980}
964981
965- static int
966- parse_item (enum pipe_metadata_msg * out_msg , struct pipe_metadata_prepared * prepared , const char * item )
967- {
968- struct input_metadata * m = & prepared -> input_metadata ;
969- uint32_t type ;
970- uint32_t code ;
971- uint8_t * data ;
972- int data_len ;
973- char * * dstptr ;
974- enum pipe_metadata_msg message ;
975- int ret ;
976-
977- ret = parse_item_xml (& type , & code , & data , & data_len , item );
978- if (ret < 0 )
979- return -1 ;
980-
981- dstptr = NULL ;
982- message = PIPE_METADATA_MSG_METADATA ;
983-
984- if (code == dmap_str2val ("asal" ))
985- dstptr = & m -> album ;
986- else if (code == dmap_str2val ("asar" ))
987- dstptr = & m -> artist ;
988- else if (code == dmap_str2val ("minm" ))
989- dstptr = & m -> title ;
990- else if (code == dmap_str2val ("asgn" ))
991- dstptr = & m -> genre ;
992- else if (code == dmap_str2val ("prgr" ))
993- message = PIPE_METADATA_MSG_PROGRESS ;
994- else if (code == dmap_str2val ("pvol" ))
995- message = PIPE_METADATA_MSG_VOLUME ;
996- else if (code == dmap_str2val ("PICT" ))
997- message = PIPE_METADATA_MSG_PICTURE ;
998- else if (code == dmap_str2val ("pfls" ))
999- message = PIPE_METADATA_MSG_FLUSH ;
1000- else
1001- goto ignore ;
1002-
1003- if (message != PIPE_METADATA_MSG_FLUSH && (!data || data_len == 0 ))
1004- {
1005- log_incoming (E_DBG , "Missing or pending Shairport metadata payload" , type , code , data_len );
1006- goto ignore ;
1007- }
1008-
1009- ret = 0 ;
1010- if (message == PIPE_METADATA_MSG_PROGRESS )
1011- ret = parse_progress (prepared , (char * )data );
1012- else if (message == PIPE_METADATA_MSG_VOLUME )
1013- ret = parse_volume (prepared , (char * )data );
1014- else if (message == PIPE_METADATA_MSG_PICTURE )
1015- ret = parse_picture (prepared , data , data_len );
1016- else if (dstptr )
1017- swap_pointers (dstptr , (char * * )& data );
1018-
1019- if (ret < 0 )
1020- goto ignore ;
1021-
1022- log_incoming (E_DBG , "Applying Shairport metadata" , type , code , data_len );
1023-
1024- * out_msg = message ;
1025- free (data );
1026- return 0 ;
1027-
1028- ignore :
1029- * out_msg = 0 ;
1030- free (data );
1031- return 0 ;
1032- }
1033-
1034982// Music Assistant terminates commands/metadata with a newline. This extracts
1035983// one item from the evbuffer, or NULL if there is no complete item.
1036984static char *
@@ -1092,6 +1040,7 @@ pipe_read_cb(evutil_socket_t fd, short event, void *arg)
10921040{
10931041 struct pipe * pipe = arg ;
10941042 struct player_status status ;
1043+ struct player_speaker_info speaker_info ;
10951044 int ret ;
10961045
10971046 ret = player_get_status (& status );
@@ -1287,6 +1236,14 @@ pipe_metadata_read_cb(evutil_socket_t fd, short event, void *arg)
12871236 DPRINTF (E_DBG , L_PLAYER , "%s:Setting volume from metadata pipe to %d\n" , __func__ , pipe_metadata .prepared .volume );
12881237 player_volume_set (pipe_metadata .prepared .volume );
12891238 }
1239+ if (message & PIPE_METADATA_MSG_PIN ) {
1240+ DPRINTF (E_DBG , L_PLAYER , "%s:Setting PIN from metadata pipe to %s\n" , __func__ , pipe_metadata .prepared .pin );
1241+ // We only support AirPlay2 at the moment. The below code will need to be changed if we add support
1242+ // for RAOP.
1243+ player_verification_kickoff (& pipe_metadata .prepared .pin , OUTPUT_TYPE_AIRPLAY );
1244+ free (pipe_metadata .prepared .pin );
1245+
1246+ }
12901247 if (message & PIPE_METADATA_MSG_FLUSH ) {
12911248 DPRINTF (E_DBG , L_PLAYER , "%s:Flushing playback from metadata pipe command\n" , __func__ );
12921249 player_playback_flush ();
@@ -1635,6 +1592,9 @@ mass_timer_cb(int fd, short what, void *arg)
16351592int
16361593mass_init (void )
16371594{
1595+ // Maybe we can add a call to player_device_add(device) in here somewhere to initiate device connection before
1596+ // audio is streamed to the named pipe. Currently, device connection is initiatied on receipt of data on the
1597+ // audio named pipe.
16381598 DPRINTF (E_DBG , L_PLAYER , "mass_init()\n" );
16391599
16401600 CHECK_ERR (L_PLAYER , mutex_init (& pipe_metadata .prepared .lock ));
@@ -1644,6 +1604,8 @@ mass_init(void)
16441604 if (!tid_pipe ) {
16451605 // main thread
16461606 // Create a persistent event timer in the main event loop to monitor and report playback status
1607+ // Using the main thread may not be necessary anymore - test moving this into the pipe thread at some time
1608+ // to understand the implications.
16471609 mass_timer_event = event_new (evbase_main , -1 , EV_PERSIST | EV_TIMEOUT , mass_timer_cb , NULL );
16481610 DPRINTF (E_DBG , L_PLAYER ,
16491611 "%s:Activating persistent event timer with timeval %d sec, %d usec\n," ,
0 commit comments