diff --git a/common.h b/common.h index 9f323e1b..639c1c0f 100644 --- a/common.h +++ b/common.h @@ -141,6 +141,7 @@ typedef struct { int metadata_sockport; size_t metadata_sockmsglength; int get_coverart; + double metadata_progress_interval; // 0 means no progress reports #endif #ifdef CONFIG_MQTT int mqtt_enabled; diff --git a/player.c b/player.c index ab85cb7b..ebab2c01 100644 --- a/player.c +++ b/player.c @@ -1837,7 +1837,9 @@ void player_thread_cleanup_handler(void *arg) { void *player_thread_func(void *arg) { rtsp_conn_info *conn = (rtsp_conn_info *)arg; - +#ifdef CONFIG_METADATA + uint64_t time_of_last_metadata_progress_update; +#endif uint64_t previous_frames_played = 0; // initialised to avoid a "possibly uninitialised" warning uint64_t previous_raw_measurement_time = 0; // initialised to avoid a "possibly uninitialised" warning @@ -2197,7 +2199,7 @@ void *player_thread_func(void *arg) { debug(2, "Play begin"); while (1) { - + int this_is_the_first_frame = 0; //will be set if it is // check a few parameters to ensure they are non-zero if (config.output_rate == 0) debug(1, "config.output_rate is zero!"); @@ -2690,6 +2692,7 @@ void *player_thread_func(void *arg) { if (at_least_one_frame_seen_this_session == 0) { at_least_one_frame_seen_this_session = 1; + this_is_the_first_frame = 1; // debug(2,"first frame real sync error (positive --> late): %" PRId64 " frames.", // sync_error); @@ -2740,11 +2743,10 @@ void *player_thread_func(void *arg) { sync_error = 0; // say the error was fixed! } // since this is the first frame of audio, inform the user if requested... - #ifdef CONFIG_AIRPLAY_2 if (conn->airplay_stream_type == realtime_stream) { if (conn->airplay_type == ap_1) { -#ifdef CONFIG_METADATA_HUB +#ifdef CONFIG_METADATA send_ssnc_metadata('styp', "Classic", strlen("Classic"), 1); #endif if (config.statistics_requested) @@ -2752,7 +2754,7 @@ void *player_thread_func(void *arg) { " -- AirPlay 1 Compatible.", conn->connection_number, inframe->given_timestamp); } else { -#ifdef CONFIG_METADATA_HUB +#ifdef CONFIG_METADATA send_ssnc_metadata('styp', "Realtime", strlen("Realtime"), 1); #endif if (config.statistics_requested) @@ -2761,7 +2763,7 @@ void *player_thread_func(void *arg) { conn->connection_number, inframe->given_timestamp); } } else { -#ifdef CONFIG_METADATA_HUB +#ifdef CONFIG_METADATA send_ssnc_metadata('styp', "Buffered", strlen("Buffered"), 1); #endif if (config.statistics_requested) @@ -2770,7 +2772,7 @@ void *player_thread_func(void *arg) { conn->connection_number, inframe->given_timestamp); } #else -#ifdef CONFIG_METADATA_HUB +#ifdef CONFIG_METADATA send_ssnc_metadata('styp', "Classic", strlen("Classic"), 1); #endif if (config.statistics_requested) @@ -3042,6 +3044,29 @@ void *player_thread_func(void *arg) { } uint64_t should_be_time; frame_to_local_time(inframe->given_timestamp, &should_be_time, conn); + +#ifdef CONFIG_METADATA + if (config.metadata_progress_interval != 0.0) { + char hb[128]; + if (this_is_the_first_frame != 0) { + memset(hb, 0, 128); + snprintf(hb, 127, "%" PRIu32 "/%" PRId64 "", inframe->given_timestamp, should_be_time); + send_ssnc_metadata('phbt', hb, strlen(hb), 1); + time_of_last_metadata_progress_update = local_time_now; + } else { + uint64_t mx = 1000000000; + uint64_t iv = config.metadata_progress_interval * mx; + iv = iv + time_of_last_metadata_progress_update; + int64_t delta = iv - local_time_now; + if (delta <= 0) { + memset(hb, 0, 128); + snprintf(hb, 127, "%" PRIu32 "/%" PRId64 "", inframe->given_timestamp, should_be_time); + send_ssnc_metadata('phbt', hb, strlen(hb), 1); + time_of_last_metadata_progress_update = local_time_now; + } + } + } +#endif config.output->play(conn->outbuf, play_samples, play_samples_are_timed, inframe->given_timestamp, should_be_time); } @@ -3075,6 +3100,7 @@ void *player_thread_func(void *arg) { // if this is the first frame, see if it's close to when it's supposed to be // release, which will be its time plus latency and any offset_time if (at_least_one_frame_seen_this_session == 0) { + this_is_the_first_frame = 1; at_least_one_frame_seen_this_session = 1; } @@ -3090,6 +3116,28 @@ void *player_thread_func(void *arg) { } uint64_t should_be_time; frame_to_local_time(inframe->given_timestamp, &should_be_time, conn); +#ifdef CONFIG_METADATA + if (config.metadata_progress_interval != 0.0) { + char hb[128]; + if (this_is_the_first_frame != 0) { + memset(hb, 0, 128); + snprintf(hb, 127, "%" PRIu32 "/%" PRId64 "", inframe->given_timestamp, should_be_time); + send_ssnc_metadata('phbt', hb, strlen(hb), 1); + time_of_last_metadata_progress_update = local_time_now; + } else { + uint64_t mx = 1000000000; + uint64_t iv = config.metadata_progress_interval * mx; + iv = iv + time_of_last_metadata_progress_update; + int64_t delta = iv - local_time_now; + if (delta <= 0) { + memset(hb, 0, 128); + snprintf(hb, 127, "%" PRIu32 "/%" PRId64 "", inframe->given_timestamp, should_be_time); + send_ssnc_metadata('phbt', hb, strlen(hb), 1); + time_of_last_metadata_progress_update = local_time_now; + } + } + } +#endif config.output->play(conn->outbuf, play_samples, play_samples_are_timed, inframe->given_timestamp, should_be_time); } diff --git a/shairport.c b/shairport.c index 3503deb7..a5e521ff 100644 --- a/shairport.c +++ b/shairport.c @@ -998,6 +998,11 @@ int parse_options(int argc, char **argv) { if (config_lookup_string(config.cfg, "metadata.pipe_name", &str)) { config.metadata_pipename = (char *)str; } + + if (config_lookup_float(config.cfg, "metadata.progress_interval", &dvalue)) { + config.metadata_progress_interval = dvalue; + } + if (config_lookup_string(config.cfg, "metadata.socket_address", &str)) { config.metadata_sockaddr = (char *)str;