4.1.1 almost...

This commit is contained in:
Mike Brady
2022-12-05 10:25:48 +00:00
28 changed files with 652 additions and 239 deletions
@@ -7,7 +7,7 @@ on:
jobs:
build:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
+1 -1
View File
@@ -7,7 +7,7 @@ on:
jobs:
build:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
@@ -7,7 +7,7 @@ on:
jobs:
build:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
+1 -1
View File
@@ -7,7 +7,7 @@ on:
jobs:
build:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
@@ -7,7 +7,7 @@ on:
jobs:
build:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
@@ -7,7 +7,7 @@ on:
jobs:
build:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
+2 -1
View File
@@ -23,8 +23,9 @@ Do this until no more copies of `shairport-sync` are found.
You should also remove any of the following service files that may be present:
* `/etc/systemd/system/shairport-sync.service`
* `/etc/systemd/user/shairport-sync.service`
* `/lib/systemd/system/shairport-sync.service`
* `/lib/systemd/user/shairport-sync.service`
* `/etc/init.d/shairport-sync`
* `/etc/init.d/shairport-sync`
New service files will be installed if necessary at the `# make install` stage.
#### Reboot after Cleaning Up
+4 -2
View File
@@ -56,7 +56,8 @@ pthread_mutex_t activity_monitor_mutex;
pthread_cond_t activity_monitor_cv;
void going_active(int block) {
// debug(1, "activity_monitor: state transitioning to \"active\" with%s blocking", block ? "" : "out");
// debug(1, "activity_monitor: state transitioning to \"active\" with%s blocking", block ? "" :
// "out");
if (config.cmd_active_start)
command_execute(config.cmd_active_start, "", block);
#ifdef CONFIG_METADATA
@@ -75,7 +76,8 @@ void going_active(int block) {
}
void going_inactive(int block) {
// debug(1, "activity_monitor: state transitioning to \"inactive\" with%s blocking", block ? "" : "out");
// debug(1, "activity_monitor: state transitioning to \"inactive\" with%s blocking", block ? "" :
// "out");
if (config.cmd_active_stop)
command_execute(config.cmd_active_stop, "", block);
#ifdef CONFIG_METADATA
+3 -1
View File
@@ -189,7 +189,9 @@ static int precision_delay_available() {
// this is very crude -- if the device is a hardware device, then it's assumed the delay is
// precise
const char *output_device_name = snd_pcm_name(alsa_handle);
int is_a_real_hardware_device = (strstr(output_device_name, "hw:") == output_device_name);
int is_a_real_hardware_device = 0;
if (output_device_name != NULL)
is_a_real_hardware_device = (strstr(output_device_name, "hw:") == output_device_name);
// The criteria as to whether precision delay is available
// is whether the device driver returns non-zero update timestamps
+106 -67
View File
@@ -155,12 +155,11 @@ static int init(__attribute__((unused)) int argc, __attribute__((unused)) char *
static void deinit(void) {
pa_threaded_mainloop_stop(mainloop);
pa_threaded_mainloop_free(mainloop);
// debug(1, "pa deinit done");
debug(1, "pa deinit done");
}
static void start(__attribute__((unused)) int sample_rate,
__attribute__((unused)) int sample_format) {
static void do_start() {
// debug(1, "pa_start");
uint32_t buffer_size_in_bytes = (uint32_t)2 * 2 * RATE * 0.1; // hard wired in here
// debug(1, "pa_buffer size is %u bytes.", buffer_size_in_bytes);
@@ -223,88 +222,128 @@ static void start(__attribute__((unused)) int sample_rate,
pa_threaded_mainloop_unlock(mainloop);
}
static int play(void *buf, int samples, __attribute__((unused)) int sample_type,
__attribute__((unused)) uint32_t timestamp,
__attribute__((unused)) uint64_t playtime) {
// debug(1,"pa_play of %d samples.",samples);
// copy the samples into the queue
size_t bytes_to_transfer = samples * 2 * 2;
size_t space_to_end_of_buffer = audio_umb - audio_eoq;
if (space_to_end_of_buffer >= bytes_to_transfer) {
memcpy(audio_eoq, buf, bytes_to_transfer);
audio_occupancy += bytes_to_transfer;
pthread_mutex_lock(&buffer_mutex);
audio_eoq += bytes_to_transfer;
pthread_mutex_unlock(&buffer_mutex);
} else {
memcpy(audio_eoq, buf, space_to_end_of_buffer);
buf += space_to_end_of_buffer;
memcpy(audio_lmb, buf, bytes_to_transfer - space_to_end_of_buffer);
pthread_mutex_lock(&buffer_mutex);
audio_occupancy += bytes_to_transfer;
pthread_mutex_unlock(&buffer_mutex);
audio_eoq = audio_lmb + bytes_to_transfer - space_to_end_of_buffer;
}
if ((audio_occupancy >= 11025 * 2 * 2) && (pa_stream_is_corked(stream))) {
// debug(1,"Uncorked");
pa_threaded_mainloop_lock(mainloop);
pa_stream_cork(stream, 0, stream_success_cb, mainloop);
pa_threaded_mainloop_unlock(mainloop);
}
return 0;
static void start(__attribute__((unused)) int sample_rate,
__attribute__((unused)) int sample_format) {
do_start();
}
int pa_delay(long *the_delay) {
long result = 0;
int reply = -ENODEV;
static int stream_is_open() {
int response = 0; // default to no
pa_usec_t latency;
int negative;
pa_threaded_mainloop_lock(mainloop);
int gl = pa_stream_get_latency(stream, &latency, &negative);
pa_threaded_mainloop_unlock(mainloop);
if (gl == PA_ERR_NODATA) {
// debug(1, "No latency data yet.");
reply = -ENODEV;
} else if (gl != 0) {
// debug(1,"Error %d getting latency.",gl);
reply = -EIO;
if (gl >= 0)
response = 1;
return response;
}
static int play(void *buf, int samples, __attribute__((unused)) int sample_type,
__attribute__((unused)) uint32_t timestamp,
__attribute__((unused)) uint64_t playtime) {
// debug(1,"pa_play of %d samples.",samples);
if (stream_is_open() == 0) {
// debug(1,"pa open stream before play.");
do_start();
}
if (stream_is_open() != 0) {
// copy the samples into the queue
size_t bytes_to_transfer = samples * 2 * 2;
size_t space_to_end_of_buffer = audio_umb - audio_eoq;
if (space_to_end_of_buffer >= bytes_to_transfer) {
memcpy(audio_eoq, buf, bytes_to_transfer);
audio_occupancy += bytes_to_transfer;
pthread_mutex_lock(&buffer_mutex);
audio_eoq += bytes_to_transfer;
pthread_mutex_unlock(&buffer_mutex);
} else {
memcpy(audio_eoq, buf, space_to_end_of_buffer);
buf += space_to_end_of_buffer;
memcpy(audio_lmb, buf, bytes_to_transfer - space_to_end_of_buffer);
pthread_mutex_lock(&buffer_mutex);
audio_occupancy += bytes_to_transfer;
pthread_mutex_unlock(&buffer_mutex);
audio_eoq = audio_lmb + bytes_to_transfer - space_to_end_of_buffer;
}
if ((audio_occupancy >= 11025 * 2 * 2) && (pa_stream_is_corked(stream))) {
// debug(1,"Uncorked");
pa_threaded_mainloop_lock(mainloop);
pa_stream_cork(stream, 0, stream_success_cb, mainloop);
pa_threaded_mainloop_unlock(mainloop);
}
} else {
result = (audio_occupancy / (2 * 2)) + (latency * 44100) / 1000000;
reply = 0;
// debug(2, "could not open pa stream for play");
}
return 0;
}
int pa_delay(long *the_delay) {
// debug(1,"pa_delay");
int reply = -ENODEV;
long result = 0;
if (stream_is_open() == 0) {
// debug(1,"pa open stream before delay.");
do_start();
}
if (stream_is_open() != 0) {
pa_usec_t latency;
int negative;
pa_threaded_mainloop_lock(mainloop);
int gl = pa_stream_get_latency(stream, &latency, &negative);
pa_threaded_mainloop_unlock(mainloop);
if (gl == PA_ERR_NODATA) {
// debug(1, "No latency data yet.");
reply = -ENODEV;
} else if (gl != 0) {
// debug(1,"Error %d getting latency.",gl);
reply = -EIO;
} else {
result = (audio_occupancy / (2 * 2)) + (latency * 44100) / 1000000;
reply = 0;
}
} else {
// debug(2, "could not open pa stream for delay");
}
*the_delay = result;
return reply;
}
void flush(void) {
// Cork the stream so it will stop playing
pa_threaded_mainloop_lock(mainloop);
if (pa_stream_is_corked(stream) == 0) {
// debug(1,"Flush and cork for flush.");
pa_stream_flush(stream, stream_success_cb, NULL);
pa_stream_cork(stream, 1, stream_success_cb, mainloop);
// debug(1,"Flush.");
if (stream_is_open() != 0) {
// Cork the stream so it will stop playing
pa_threaded_mainloop_lock(mainloop);
if (pa_stream_is_corked(stream) == 0) {
// debug(1,"Flush and cork for flush.");
pa_stream_flush(stream, stream_success_cb, NULL);
pa_stream_cork(stream, 1, stream_success_cb, mainloop);
}
pa_threaded_mainloop_unlock(mainloop);
audio_toq = audio_eoq = audio_lmb;
audio_umb = audio_lmb + audio_size;
audio_occupancy = 0;
}
pa_threaded_mainloop_unlock(mainloop);
audio_toq = audio_eoq = audio_lmb;
audio_umb = audio_lmb + audio_size;
audio_occupancy = 0;
}
static void stop(void) {
// Cork the stream so it will stop playing
pa_threaded_mainloop_lock(mainloop);
if (pa_stream_is_corked(stream) == 0) {
// debug(1,"Flush and cork for stop.");
pa_stream_flush(stream, stream_success_cb, NULL);
pa_stream_cork(stream, 1, stream_success_cb, mainloop);
}
pa_threaded_mainloop_unlock(mainloop);
audio_toq = audio_eoq = audio_lmb;
audio_umb = audio_lmb + audio_size;
audio_occupancy = 0;
// debug(1,"Stop.");
if (stream_is_open() != 0) {
// Cork the stream so it will stop playing
pa_threaded_mainloop_lock(mainloop);
if (pa_stream_is_corked(stream) == 0) {
// debug(1,"Flush and cork for stop.");
pa_stream_flush(stream, stream_success_cb, NULL);
pa_stream_cork(stream, 1, stream_success_cb, mainloop);
}
pa_threaded_mainloop_unlock(mainloop);
audio_toq = audio_eoq = audio_lmb;
audio_umb = audio_lmb + audio_size;
audio_occupancy = 0;
// debug(1,"pa stop");
pa_stream_disconnect(stream);
// debug(1,"pa stop");
pa_stream_disconnect(stream);
}
}
audio_output audio_pa = {.name = "pa",
+3 -1
View File
@@ -141,7 +141,9 @@ static void deinit(void) {
close(fd);
}
static void help(void) { printf(" Provide the pipe's pathname. The default is \"%s\".\n", default_pipe_name); }
static void help(void) {
printf(" Provide the pipe's pathname. The default is \"%s\".\n", default_pipe_name);
}
audio_output audio_pipe = {.name = "pipe",
.help = &help,
+2
View File
@@ -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;
@@ -159,6 +160,7 @@ typedef struct {
int mqtt_enable_remote;
char *mqtt_empty_payload_substitute;
#endif
uint8_t ap1_prefix[6];
uint8_t hw_addr[8]; // only needs 6 but 8 is handy when converting this to a number
int port;
int udp_port_base;
+6 -1
View File
@@ -406,6 +406,8 @@ AM_CONDITIONAL([USE_METADATA], [test "x$with_metadata" = "xyes"])
AC_ARG_WITH(airplay-2, [AS_HELP_STRING([--with-airplay-2],[Build for AirPlay 2])])
if test "x$with_airplay_2" = "xyes" ; then
AC_DEFINE([CONFIG_AIRPLAY_2], 1, [Build for AirPlay 2])
AC_CHECK_PROG(XXD_CHECK,xxd,yes)
AS_IF([test x"$XXD_CHECK" != x"yes"], [AC_MSG_ERROR([xxd can not be found. Please install xxd for building for AirPlay 2.])])
PKG_CHECK_MODULES([libplist], [libplist >= 2.0.0],[CFLAGS="${libplist_CFLAGS} ${CFLAGS}" LIBS="${libplist_LIBS} ${LIBS}"],[
PKG_CHECK_MODULES([libplist], [libplist-2.0 >= 2.0.0],[CFLAGS="${libplist_CFLAGS} ${CFLAGS}" LIBS="${libplist_LIBS} ${LIBS}"],[
AC_MSG_ERROR(AirPlay 2 support requires libplist 2.0.0 or later -- search for pkg libplist-dev on Debian or libplist-2.2.0 or later on FreeBSD!)
@@ -420,7 +422,10 @@ if test "x$with_airplay_2" = "xyes" ; then
PKG_CHECK_MODULES([libavutil], [libavutil],[CFLAGS="${libavutil_CFLAGS} ${CFLAGS}" LIBS="${libavutil_LIBS} ${LIBS}"],[
AC_MSG_ERROR(AirPlay 2 support requires libavutil -- libavutil-dev suggested)])
PKG_CHECK_MODULES([libavcodec], [libavcodec],[CFLAGS="${libavcodec_CFLAGS} ${CFLAGS}" LIBS="${libavcodec_LIBS} ${LIBS}"],[
AC_MSG_ERROR(AirPlay 2 support requires libavcodec -- libavcodec-dev suggested)])
AC_CHECK_LIB([avcodec], [avcodec_find_decoder], [], [
AC_MSG_ERROR(AirPlay 2 support requires libavcodec -- libavcodec-dev suggested)
])
])
AC_CHECK_LIB([avformat],[avformat_new_stream], [], [AC_MSG_ERROR([AirPlay 2 support requires libavformat -- libavformat-dev suggested])])
AC_CHECK_LIB([swresample],[swr_convert], [], [AC_MSG_ERROR([AirPlay 2 support requires libswresample -- libswresample-dev suggested])])
AC_CHECK_LIB([uuid],[uuid_generate], [], [AC_MSG_ERROR([AirPlay 2 support requires the uuid library -- uuid-dev suggested])])
+9 -6
View File
@@ -803,8 +803,9 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) {
case 'canp': // nowplaying 4 ids: dbid, plid, playlistItem, itemid (from mellowware
// see reference above)
debug(2, "DACP Composite ID seen");
if (memcmp(metadata_store.item_composite_id, sp - item_size,
sizeof(metadata_store.item_composite_id)) != 0) {
if ((metadata_store.item_composite_id_is_valid == 0) ||
(memcmp(metadata_store.item_composite_id, sp - item_size,
sizeof(metadata_store.item_composite_id)) != 0)) {
memcpy(metadata_store.item_composite_id, sp - item_size,
sizeof(metadata_store.item_composite_id));
char st[33];
@@ -817,6 +818,7 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) {
*pt = 0;
debug(2, "Item composite ID changed to 0x%s.", st);
metadata_store.item_composite_id_changed = 1;
metadata_store.item_composite_id_is_valid = 1;
}
break;
case 'astm':
@@ -826,6 +828,7 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) {
if (ui != metadata_store.songtime_in_milliseconds) {
metadata_store.songtime_in_milliseconds = ui;
metadata_store.songtime_in_milliseconds_changed = 1;
metadata_store.songtime_in_milliseconds_is_valid = 1;
debug(2, "DACP Song Time set to: \"%u\"",
metadata_store.songtime_in_milliseconds);
}
@@ -1219,8 +1222,8 @@ int dacp_get_volume(int32_t *the_actual_volume) {
http_response = dacp_get_speaker_list((dacp_spkr_stuff *)&speaker_info, 50, &speaker_count);
if (http_response == 200) {
// get our machine number
uint16_t *hn = (uint16_t *)config.hw_addr;
uint32_t *ln = (uint32_t *)(config.hw_addr + 2);
uint16_t *hn = (uint16_t *)config.ap1_prefix;
uint32_t *ln = (uint32_t *)(config.ap1_prefix + 2);
uint64_t t1 = ntohs(*hn);
uint64_t t2 = ntohl(*ln);
int64_t machine_number = (t1 << 32) + t2; // this form is useful
@@ -1273,8 +1276,8 @@ int dacp_set_volume(int32_t vo) {
http_response = dacp_get_speaker_list((dacp_spkr_stuff *)&speaker_info, 50, &speaker_count);
if (http_response == 200) {
// get our machine number
uint16_t *hn = (uint16_t *)config.hw_addr;
uint32_t *ln = (uint32_t *)(config.hw_addr + 2);
uint16_t *hn = (uint16_t *)config.ap1_prefix;
uint32_t *ln = (uint32_t *)(config.ap1_prefix + 2);
uint64_t t1 = ntohs(*hn);
uint64_t t2 = ntohl(*ln);
int64_t machine_number = (t1 << 32) + t2; // this form is useful
+83 -7
View File
@@ -68,6 +68,8 @@ void dbus_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused))
argc->airplay_volume);
shairport_sync_remote_control_set_client(shairportSyncRemoteControlSkeleton, argc->client_ip);
shairport_sync_remote_control_set_client_name(shairportSyncRemoteControlSkeleton,
argc->client_name);
// although it's a DACP server, the server is in fact, part of the the AirPlay "client" (their
// term).
@@ -95,6 +97,34 @@ void dbus_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused))
}
}
if (argc->frame_position_string) {
// debug(1, "Check frame position string");
th = shairport_sync_get_frame_position(shairportSyncSkeleton);
if ((th == NULL) || (strcasecmp(th, argc->frame_position_string) != 0)) {
// debug(1, "Frame position string should be changed");
shairport_sync_set_frame_position(shairportSyncSkeleton, argc->frame_position_string);
}
}
if (argc->first_frame_position_string) {
// debug(1, "Check first frame position string");
th = shairport_sync_get_first_frame_position(shairportSyncSkeleton);
if ((th == NULL) || (strcasecmp(th, argc->first_frame_position_string) != 0)) {
// debug(1, "First frame position string should be changed");
shairport_sync_set_first_frame_position(shairportSyncSkeleton, argc->first_frame_position_string);
}
}
if (argc->stream_type) {
// debug(1, "Check stream type");
th = shairport_sync_remote_control_get_stream_type(shairportSyncRemoteControlSkeleton);
if ((th == NULL) || (strcasecmp(th, argc->stream_type) != 0)) {
// debug(1, "Stream type string should be changed");
shairport_sync_remote_control_set_stream_type(shairportSyncRemoteControlSkeleton,
argc->stream_type);
}
}
switch (argc->player_state) {
case PS_NOT_AVAILABLE:
shairport_sync_remote_control_set_player_state(shairportSyncRemoteControlSkeleton,
@@ -201,8 +231,8 @@ void dbus_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused))
g_variant_builder_add(dict_builder, "{sv}", "mpris:artUrl", artUrl);
}
// Add in the Track ID based on the 'mper' metadata if it is non-zero
if (argc->item_id != 0) {
// Add in the Track ID based on the 'mper' metadata if it is valid
if (argc->item_id_is_valid != 0) {
char trackidstring[128];
snprintf(trackidstring, sizeof(trackidstring), "/org/gnome/ShairportSync/%" PRIX64 "",
argc->item_id);
@@ -210,6 +240,15 @@ void dbus_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused))
g_variant_builder_add(dict_builder, "{sv}", "mpris:trackid", trackid);
}
// Add in the Song Data Kind based on the 'asdk' metadata if it is valid
// It seems that this is 0 for a timed play, e.g. a track or an album, but is 1 for an untimed
// play, such as a stream.
if (argc->song_data_kind_is_valid != 0) {
GVariant *songdatakind = g_variant_new_uint32(argc->song_data_kind);
g_variant_builder_add(dict_builder, "{sv}", "sps:songdatakind", songdatakind);
}
// Add the track name if it exists
if (argc->track_name) {
GVariant *track_name = g_variant_new("s", argc->track_name);
@@ -222,7 +261,7 @@ void dbus_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused))
g_variant_builder_add(dict_builder, "{sv}", "xesam:album", album_name);
}
// Add the artist name if it exists
// Add the artist name list if it exists
if (argc->artist_name) {
GVariantBuilder *artist_as = g_variant_builder_new(G_VARIANT_TYPE("as"));
g_variant_builder_add(artist_as, "s", argc->artist_name);
@@ -231,7 +270,25 @@ void dbus_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused))
g_variant_builder_add(dict_builder, "{sv}", "xesam:artist", artists);
}
// Add the genre if it exists
// Add the album artist list if it exists
if (argc->album_artist_name) {
GVariantBuilder *album_artist_as = g_variant_builder_new(G_VARIANT_TYPE("as"));
g_variant_builder_add(album_artist_as, "s", argc->album_artist_name);
GVariant *album_artists = g_variant_builder_end(album_artist_as);
g_variant_builder_unref(album_artist_as);
g_variant_builder_add(dict_builder, "{sv}", "xesam:albumArtist", album_artists);
}
// Add the composer list if it exists
if (argc->composer) {
GVariantBuilder *composer_as = g_variant_builder_new(G_VARIANT_TYPE("as"));
g_variant_builder_add(composer_as, "s", argc->composer);
GVariant *composers = g_variant_builder_end(composer_as);
g_variant_builder_unref(composer_as);
g_variant_builder_add(dict_builder, "{sv}", "xesam:composer", composers);
}
// Add the genre list if it exists
if (argc->genre) {
GVariantBuilder *genre_as = g_variant_builder_new(G_VARIANT_TYPE("as"));
g_variant_builder_add(genre_as, "s", argc->genre);
@@ -240,11 +297,11 @@ void dbus_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused))
g_variant_builder_add(dict_builder, "{sv}", "xesam:genre", genre);
}
if (argc->songtime_in_milliseconds) {
if (argc->songtime_in_milliseconds_is_valid != 0) {
uint64_t track_length_in_microseconds = argc->songtime_in_milliseconds;
track_length_in_microseconds *= 1000; // to microseconds in 64-bit precision
// Make up the track name and album name
// debug(1, "Set tracklength to %lu.", track_length_in_microseconds);
// debug(1, "Set tracklength to %" PRId64 ".", track_length_in_microseconds);
GVariant *tracklength = g_variant_new("x", track_length_in_microseconds);
g_variant_builder_add(dict_builder, "{sv}", "mpris:length", tracklength);
}
@@ -814,6 +871,17 @@ static gboolean on_handle_drop_session(ShairportSync *skeleton, GDBusMethodInvoc
return TRUE;
}
static gboolean on_handle_set_frame_position_update_interval(ShairportSync *skeleton,
GDBusMethodInvocation *invocation,
const gdouble seconds,
__attribute__((unused))
gpointer user_data) {
debug(1, ">> set frame position update interval to %.6f.", seconds);
config.metadata_progress_interval = seconds;
shairport_sync_complete_set_frame_position_update_interval(skeleton, invocation);
return TRUE;
}
static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name,
__attribute__((unused)) gpointer user_data) {
@@ -872,6 +940,9 @@ static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name
g_signal_connect(shairportSyncSkeleton, "handle-drop-session", G_CALLBACK(on_handle_drop_session),
NULL);
g_signal_connect(shairportSyncSkeleton, "handle-set-frame-position-update-interval",
G_CALLBACK(on_handle_set_frame_position_update_interval), NULL);
g_signal_connect(shairportSyncDiagnosticsSkeleton, "notify::verbosity",
G_CALLBACK(notify_verbosity_callback), NULL);
@@ -1019,6 +1090,11 @@ static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name
// NULL);
#endif
shairport_sync_set_service_name(SHAIRPORT_SYNC(shairportSyncSkeleton), config.service_name);
shairport_sync_set_output_rate(SHAIRPORT_SYNC(shairportSyncSkeleton), config.output_rate);
shairport_sync_set_output_format(SHAIRPORT_SYNC(shairportSyncSkeleton),
sps_format_description_string(config.output_format));
#ifdef CONFIG_AIRPLAY_2
shairport_sync_set_protocol(SHAIRPORT_SYNC(shairportSyncSkeleton), "AirPlay 2");
#else
@@ -1132,7 +1208,7 @@ void stop_dbus_service() {
if (ownerID) {
g_bus_unown_name(ownerID);
} else if (service_is_running != 0) {
debug(1, "Zero OwnerID for running \"org.gnome.ShairportSync\" dbus service.");
debug(1, "Zero OwnerID for running \"org.gnome.ShairportSync\" dbus service.");
}
service_is_running = 0;
}
+3 -1
View File
@@ -89,6 +89,8 @@ RUN apk -U add \
COPY --from=builder /shairport-sync/build/install/usr/local/bin/shairport-sync /usr/local/bin/shairport-sync
COPY --from=builder /usr/local/bin/nqptp /usr/local/bin/nqptp
COPY --from=builder /usr/local/lib/libalac.* /usr/local/lib/
COPY --from=builder /shairport-sync/build/install/etc/shairport-sync.conf /etc/
COPY --from=builder /shairport-sync/build/install/etc/shairport-sync.conf.sample /etc/
COPY --from=builder /shairport-sync/build/install/etc/dbus-1/system.d/shairport-sync-dbus.conf /etc/dbus-1/system.d/
COPY --from=builder /shairport-sync/build/install/etc/dbus-1/system.d/shairport-sync-mpris.conf /etc/dbus-1/system.d/
@@ -107,4 +109,4 @@ RUN addgroup -g 29 docker_audio && addgroup shairport-sync docker_audio && addgr
# Remove anything we don't need.
RUN rm -rf /lib/apk/db/*
ENTRYPOINT [ "/init", "s6-setuidgid", "shairport-sync", "/usr/local/bin/shairport-sync" ]
ENTRYPOINT [ "/init", "/usr/local/bin/shairport-sync" ]
+3 -1
View File
@@ -68,6 +68,8 @@ RUN apk -U add \
# Copy build files.
COPY --from=builder /shairport-sync/build/install/usr/local/bin/shairport-sync /usr/local/bin/shairport-sync
COPY --from=builder /usr/local/lib/libalac.* /usr/local/lib/
COPY --from=builder /shairport-sync/build/install/etc/shairport-sync.conf /etc/
COPY --from=builder /shairport-sync/build/install/etc/shairport-sync.conf.sample /etc/
COPY --from=builder /shairport-sync/build/install/etc/dbus-1/system.d/shairport-sync-dbus.conf /etc/dbus-1/system.d/
COPY --from=builder /shairport-sync/build/install/etc/dbus-1/system.d/shairport-sync-mpris.conf /etc/dbus-1/system.d/
@@ -86,4 +88,4 @@ RUN addgroup -g 29 docker_audio && addgroup shairport-sync docker_audio && addgr
# Remove anything we don't need.
RUN rm -rf /lib/apk/db/*
ENTRYPOINT [ "/init", "s6-setuidgid", "shairport-sync", "/usr/local/bin/shairport-sync" ]
ENTRYPOINT [ "/init", "/usr/local/bin/shairport-sync" ]
+1 -1
View File
@@ -69,7 +69,7 @@ void mdns_register(char **txt_records, char **secondary_txt_records) {
char *p = ap1_service_name;
int i;
for (i = 0; i < 6; i++) {
snprintf(p, 3, "%02X", config.hw_addr[i]);
snprintf(p, 3, "%02X", config.ap1_prefix[i]);
p += 2;
}
*p++ = '@';
+64 -12
View File
@@ -102,6 +102,7 @@ void run_metadata_watchers(void) {
// turn off changed flags
metadata_store.cover_art_pathname_changed = 0;
metadata_store.client_ip_changed = 0;
metadata_store.client_name_changed = 0;
metadata_store.server_ip_changed = 0;
metadata_store.progress_string_changed = 0;
metadata_store.item_id_changed = 0;
@@ -109,6 +110,7 @@ void run_metadata_watchers(void) {
metadata_store.artist_name_changed = 0;
metadata_store.album_artist_name_changed = 0;
metadata_store.album_name_changed = 0;
metadata_store.song_data_kind_changed = 0;
metadata_store.track_name_changed = 0;
metadata_store.genre_changed = 0;
metadata_store.comment_changed = 0;
@@ -335,7 +337,7 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
// Some metadata items are contained in one metadata packet.
// The start of the metadata packet is signalled by an 'ssnc' 'mdst' item and
// the end of it by an 'ssnc 'mden' item.
// the end of it by an 'ssnc 'mden' item.
// We don't set "changed" for them individually; instead we set it when the 'mden' token
// comes in if the metadata_packet_item_changed item is set by parsed items
// within the packet.
@@ -347,6 +349,20 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
char *cs;
if (type == 'core') {
switch (code) {
case 'asdk': {
// get the one-byte number as an unsigned number
int song_data_kind = data[0]; // one byte
song_data_kind = song_data_kind & 0xFF; // unsigned
debug(2, "MH Song Data Kind seen: \"%d\" of length %u.", song_data_kind, length);
if ((song_data_kind != metadata_store.song_data_kind) ||
(metadata_store.song_data_kind_is_valid == 0)) {
metadata_store.song_data_kind = song_data_kind;
metadata_store.song_data_kind_changed = 1;
metadata_store.song_data_kind_is_valid = 1;
debug(2, "MH Song Data Kind set to: \"%d\"", metadata_store.song_data_kind);
metadata_packet_item_changed = 1;
}
} break;
case 'mper': {
// get the 64-bit number as a uint64_t by reading two uint32_t s and combining them
uint64_t vl = ntohl(*(uint32_t *)data); // get the high order 32 bits
@@ -354,10 +370,10 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
uint64_t ul = ntohl(*(uint32_t *)(data + sizeof(uint32_t))); // and the low order 32 bits
vl = vl + ul;
debug(2, "MH Item ID seen: \"%" PRIx64 "\" of length %u.", vl, length);
if (vl != metadata_store.item_id) {
if ((vl != metadata_store.item_id) || (metadata_store.item_id_is_valid == 0)) {
metadata_store.item_id = vl;
metadata_store.item_id_changed = 1;
metadata_store.item_id_received = 1;
metadata_store.item_id_is_valid = 1;
debug(2, "MH Item ID set to: \"%" PRIx64 "\"", metadata_store.item_id);
metadata_packet_item_changed = 1;
}
@@ -365,9 +381,11 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
case 'astm': {
uint32_t ui = ntohl(*(uint32_t *)data);
debug(2, "MH Song Time seen: \"%u\" of length %u.", ui, length);
if (ui != metadata_store.songtime_in_milliseconds) {
if ((ui != metadata_store.songtime_in_milliseconds) ||
(metadata_store.songtime_in_milliseconds_is_valid == 0)) {
metadata_store.songtime_in_milliseconds = ui;
metadata_store.songtime_in_milliseconds_changed = 1;
metadata_store.songtime_in_milliseconds_is_valid = 1;
debug(2, "MH Song Time set to: \"%u\"", metadata_store.songtime_in_milliseconds);
metadata_packet_item_changed = 1;
}
@@ -549,6 +567,14 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
}
free(cs);
break;
case 'snam':
cs = strndup(data, length);
if (string_update(&metadata_store.client_name, &metadata_store.client_name_changed, cs)) {
changed = 1;
debug(2, "MH Client Name set to: \"%s\"", metadata_store.client_name);
}
free(cs);
break;
case 'prgr':
cs = strndup(data, length);
if (string_update(&metadata_store.progress_string, &metadata_store.progress_string_changed,
@@ -558,6 +584,32 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
}
free(cs);
break;
case 'phbt':
cs = strndup(data, length);
if (string_update(&metadata_store.frame_position_string,
&metadata_store.frame_position_string_changed, cs)) {
changed = 1;
debug(2, "MH Frame Position String set to: \"%s\"", metadata_store.frame_position_string);
}
free(cs);
break;
case 'phb0':
cs = strndup(data, length);
if (string_update(&metadata_store.first_frame_position_string,
&metadata_store.first_frame_position_string_changed, cs)) {
changed = 1;
debug(2, "MH First Frame Position String set to: \"%s\"", metadata_store.first_frame_position_string);
}
free(cs);
break;
case 'styp':
cs = strndup(data, length);
if (string_update(&metadata_store.stream_type, &metadata_store.stream_type_changed, cs)) {
changed = 1;
debug(2, "MH Stream Type set to: \"%s\"", metadata_store.stream_type);
}
free(cs);
break;
case 'svip':
cs = strndup(data, length);
if (string_update(&metadata_store.server_ip, &metadata_store.server_ip_changed, cs)) {
@@ -591,14 +643,14 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
changed = (metadata_store.player_state != PS_PAUSED);
metadata_store.player_state = PS_PAUSED;
break;
/*
// not using this anymore.
case 'pffr': // this is sent when the first frame has been received
case 'prsm':
changed = (metadata_store.player_state != PS_PLAYING);
metadata_store.player_state = PS_PLAYING;
break;
*/
/*
// not using this anymore.
case 'pffr': // this is sent when the first frame has been received
case 'prsm':
changed = (metadata_store.player_state != PS_PLAYING);
metadata_store.player_state = PS_PLAYING;
break;
*/
case 'pvol': {
// Note: it's assumed that the config.airplay volume has already been correctly set.
// int32_t actual_volume;
+22 -2
View File
@@ -43,12 +43,24 @@ typedef struct metadata_bundle {
// server
int client_ip_changed;
char *client_name; // the name of the client device, if available
int client_name_changed;
char *server_ip; // IP number used by Shairport Sync
int server_ip_changed;
char *stream_type; // Realtime or Buffered
int stream_type_changed;
char *progress_string; // progress string, emitted by the source from time to time
int progress_string_changed;
char *frame_position_string; // frame position string emitted by SPS on request
int frame_position_string_changed;
char *first_frame_position_string; // first frame position string emitted by SPS on request
int first_frame_position_string_changed;
int player_thread_active; // true if a play thread is running
int dacp_server_active; // true if there's a reachable DACP server (assumed to be the Airplay
// client) ; false otherwise
@@ -67,15 +79,21 @@ typedef struct metadata_bundle {
char *cover_art_pathname;
int cover_art_pathname_changed;
uint64_t item_id; // seems to be a track ID -- see itemid in DACP.c
int item_id_changed;
int item_id_received; // important for deciding if the track information should be ignored.
int item_id_is_valid;
unsigned char
item_composite_id[16]; // seems to be nowplaying 4 ids: dbid, plid, playlistItem, itemid
int item_composite_id_changed;
int item_composite_id_is_valid;
int song_data_kind;
int song_data_kind_changed;
int song_data_kind_is_valid;
char *track_name;
int track_name_changed;
@@ -120,6 +138,8 @@ typedef struct metadata_bundle {
uint32_t songtime_in_milliseconds;
int songtime_in_milliseconds_changed;
int songtime_in_milliseconds_is_valid;
// end
+2 -2
View File
@@ -174,7 +174,7 @@ void mpris_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused)
}
// Add in the Track ID based on the 'mper' metadata if it is non-zero
if (argc->item_id != 0) {
if (argc->item_id_is_valid != 0) {
char trackidstring[128];
snprintf(trackidstring, sizeof(trackidstring), "/org/gnome/ShairportSync/%" PRIX64 "",
argc->item_id);
@@ -212,7 +212,7 @@ void mpris_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused)
g_variant_builder_add(dict_builder, "{sv}", "xesam:genre", genre);
}
if (argc->songtime_in_milliseconds) {
if (argc->songtime_in_milliseconds_is_valid) {
uint64_t track_length_in_microseconds = argc->songtime_in_milliseconds;
track_length_in_microseconds *= 1000; // to microseconds in 64-bit precision
// Make up the track name and album name
+16
View File
@@ -178,6 +178,10 @@ void mqtt_process_metadata(uint32_t type, uint32_t code, char *data, uint32_t le
case 'asal':
mqtt_publish("songalbum", data, length);
break;
case 'asdk':
mqtt_publish("songdatakind", data,
length); // 0 seem to be a timed item, 1 an untimed stream
break;
case 'clip':
mqtt_publish("client_ip", data, length);
break;
@@ -193,6 +197,12 @@ void mqtt_process_metadata(uint32_t type, uint32_t code, char *data, uint32_t le
case 'daid':
mqtt_publish("dacp_id", data, length);
break;
case 'ofmt':
mqtt_publish("output_format", data, length);
break;
case 'ofps':
mqtt_publish("output_frame_rate", data, length);
break;
case 'pbeg':
mqtt_publish("play_start", data, length);
break;
@@ -216,9 +226,15 @@ void mqtt_process_metadata(uint32_t type, uint32_t code, char *data, uint32_t le
case 'snam':
mqtt_publish("client_name", data, length);
break;
case 'styp':
mqtt_publish("stream_type", data, length);
break;
case 'svip':
mqtt_publish("server_ip", data, length);
break;
case 'svna':
mqtt_publish("service_name", data, length);
break;
}
}
}
+10
View File
@@ -24,6 +24,14 @@
<property name="Version" type="s" access="read" />
<property name="VersionString" type="s" access="read" />
<property name="Protocol" type="s" access="read" />
<property name="ServiceName" type="s" access="read" />
<property name="OutputFormat" type="s" access="read" />
<property name="OutputRate" type="i" access="read" />
<property name="FirstFramePosition" type="s" access="read" />
<property name="FramePosition" type="s" access="read" />
<method name="SetFramePositionUpdateInterval">
<arg name="seconds" type="d" direction="in" />
</method>
</interface>
<interface name="org.gnome.ShairportSync.Diagnostics">
<property name="Verbosity" type="i" access="readwrite" />
@@ -49,6 +57,8 @@
<property name='PlayerState' type='s' access='read'/>
<property name='ProgressString' type='s' access='read'/>
<property name='Client' type='s' access='read'/>
<property name='ClientName' type='s' access='read'/>
<property name='StreamType' type='s' access='read'/>
<property name='AirplayVolume' type='d' access='read'/>
<method name="SetAirplayVolume">
<arg name="volume" type="d" direction="in" />
+122 -23
View File
@@ -363,20 +363,35 @@ static void terminate_decoders(rtsp_conn_info *conn) {
#endif
}
uint64_t buffers_allocated = 0;
uint64_t buffers_released = 0;
static void init_buffer(rtsp_conn_info *conn) {
// debug(1,"input_bytes_per_frame: %d.", conn->input_bytes_per_frame);
// debug(1,"input_bit_depth: %d.", conn->input_bit_depth);
int i;
for (i = 0; i < BUFFER_FRAMES; i++)
for (i = 0; i < BUFFER_FRAMES; i++) {
// conn->audio_buffer[i].data = malloc(conn->input_bytes_per_frame *
// conn->max_frames_per_packet);
conn->audio_buffer[i].data = malloc(8 * conn->max_frames_per_packet); // todo
void *allocation = malloc(8 * conn->max_frames_per_packet);
if (allocation == NULL) {
die("could not allocate memory for audio buffers. %" PRId64 " buffers allocated, %" PRId64
" buffers released.",
buffers_allocated, buffers_released);
} else {
conn->audio_buffer[i].data = allocation;
buffers_allocated++;
}
}
}
static void free_audio_buffers(rtsp_conn_info *conn) {
int i;
for (i = 0; i < BUFFER_FRAMES; i++)
for (i = 0; i < BUFFER_FRAMES; i++) {
free(conn->audio_buffer[i].data);
buffers_released++;
}
debug(1, "%" PRId64 " buffers allocated, %" PRId64 " buffers released.", buffers_allocated,
buffers_released);
}
int first_possibly_missing_frame = -1;
@@ -1718,14 +1733,14 @@ void player_thread_cleanup_handler(void *arg) {
int64_t elapsedMin = (time_playing / 60) % 60;
int64_t elapsedSec = time_playing % 60;
if (conn->frame_rate_valid)
inform("Connection %d: Playback Stopped. Total playing time %02" PRId64 ":%02" PRId64
inform("Connection %d: Playback stopped. Total playing time %02" PRId64 ":%02" PRId64
":%02" PRId64 ". "
"Output: %0.2f (raw), %0.2f (corrected) "
"frames per second.",
conn->connection_number, elapsedHours, elapsedMin, elapsedSec, conn->raw_frame_rate,
conn->corrected_frame_rate);
else
inform("Connection %d: Playback Stopped. Total playing time %02" PRId64 ":%02" PRId64
inform("Connection %d: Playback stopped. Total playing time %02" PRId64 ":%02" PRId64
":%02" PRId64 ".",
conn->connection_number, elapsedHours, elapsedMin, elapsedSec);
}
@@ -1738,8 +1753,8 @@ void player_thread_cleanup_handler(void *arg) {
#endif
// four possibilities
// 1 -- regular AirPlay 1
// 2 -- AirPlay 2 in AirPlay 1 mode
// 1 -- Classic Airplay -- "AirPlay 1"
// 2 -- AirPlay 2 in Classic Airplay mode
// 3 -- AirPlay 2 in Buffered Audio Mode
// 4 -- AirPlay 3 in Realtime Audio Mode.
@@ -1822,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
@@ -2182,7 +2199,9 @@ void *player_thread_func(void *arg) {
debug(2, "Play begin");
while (1) {
#ifdef CONFIG_METADATA
int this_is_the_first_frame = 0; // will be set if it is
#endif
// check a few parameters to ensure they are non-zero
if (config.output_rate == 0)
debug(1, "config.output_rate is zero!");
@@ -2675,6 +2694,9 @@ void *player_thread_func(void *arg) {
if (at_least_one_frame_seen_this_session == 0) {
at_least_one_frame_seen_this_session = 1;
#ifdef CONFIG_METADATA
this_is_the_first_frame = 1;
#endif
// debug(2,"first frame real sync error (positive --> late): %" PRId64 " frames.",
// sync_error);
@@ -2725,23 +2747,42 @@ 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...
if (config.statistics_requested) {
#ifdef CONFIG_AIRPLAY_2
if (conn->airplay_stream_type == realtime_stream) {
if (conn->airplay_type == ap_1)
inform("Connection %d: Playback Started -- AirPlay 1 Compatible.",
conn->connection_number);
else
inform("Connection %d: Playback Started -- AirPlay 2 Realtime.",
conn->connection_number);
} else {
inform("Connection %d: Playback Started -- AirPlay 2 Buffered.",
conn->connection_number);
}
#else
inform("Connection %d: Playback Started -- AirPlay 1.", conn->connection_number);
if (conn->airplay_stream_type == realtime_stream) {
if (conn->airplay_type == ap_1) {
#ifdef CONFIG_METADATA
send_ssnc_metadata('styp', "Classic", strlen("Classic"), 1);
#endif
if (config.statistics_requested)
inform("Connection %d: Playback started at frame %" PRId64
" -- Classic AirPlay (\"AirPlay 1\") Compatible.",
conn->connection_number, inframe->given_timestamp);
} else {
#ifdef CONFIG_METADATA
send_ssnc_metadata('styp', "Realtime", strlen("Realtime"), 1);
#endif
if (config.statistics_requested)
inform("Connection %d: Playback started at frame %" PRId64
" -- AirPlay 2 Realtime.",
conn->connection_number, inframe->given_timestamp);
}
} else {
#ifdef CONFIG_METADATA
send_ssnc_metadata('styp', "Buffered", strlen("Buffered"), 1);
#endif
if (config.statistics_requested)
inform("Connection %d: Playback started at frame %" PRId64
" -- AirPlay 2 Buffered.",
conn->connection_number, inframe->given_timestamp);
}
#else
#ifdef CONFIG_METADATA
send_ssnc_metadata('styp', "Classic", strlen("Classic"), 1);
#endif
if (config.statistics_requested)
inform("Connection %d: Playback started at frame %" PRId64 " -- Classic AirPlay (\"AirPlay 1\").",
conn->connection_number, inframe->given_timestamp);
#endif
}
// not too sure if abs() is implemented for int64_t, so we'll do it manually
int64_t abs_sync_error = sync_error;
@@ -3007,8 +3048,36 @@ void *player_thread_func(void *arg) {
}
uint64_t should_be_time;
frame_to_local_time(inframe->given_timestamp, &should_be_time, conn);
config.output->play(conn->outbuf, play_samples, play_samples_are_timed,
inframe->given_timestamp, should_be_time);
#ifdef CONFIG_METADATA
// debug(1,"config.metadata_progress_interval is %f.",
// config.metadata_progress_interval);
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('phb0', hb, strlen(hb), 1);
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
}
}
@@ -3040,6 +3109,9 @@ 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) {
#ifdef CONFIG_METADATA
this_is_the_first_frame = 1;
#endif
at_least_one_frame_seen_this_session = 1;
}
@@ -3057,6 +3129,33 @@ void *player_thread_func(void *arg) {
frame_to_local_time(inframe->given_timestamp, &should_be_time, conn);
config.output->play(conn->outbuf, play_samples, play_samples_are_timed,
inframe->given_timestamp, should_be_time);
#ifdef CONFIG_METADATA
// debug(1,"config.metadata_progress_interval is %f.",
// config.metadata_progress_interval);
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('phb0', hb, strlen(hb), 1);
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
}
}
+2 -2
View File
@@ -2842,12 +2842,12 @@ void *rtp_buffered_audio_processor(void *arg) {
// https://libsodium.gitbook.io/doc/secret-key_cryptography/aead/chacha20-poly1305/ietf_chacha20-poly1305_construction
// Note: the eight-byte nonce must be front-padded out to 12 bytes.
response = crypto_aead_chacha20poly1305_ietf_decrypt(
m + 7, // m
&new_payload_length, // mlen_p
NULL, // nsec,
packet + 12, // the ciphertext starts 12 bytes in and is followed by the MAC tag,
packet + 12, // the ciphertext starts 12 bytes in and is followed by the MAC tag,
nread - (8 + 12), // clen -- the last 8 bytes are the nonce
packet + 4, // authenticated additional data
8, // authenticated additional data length
+132 -77
View File
@@ -47,6 +47,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <netinet/tcp.h>
#include <sys/ioctl.h>
@@ -1219,12 +1220,18 @@ ssize_t timed_read_from_rtsp_connection(rtsp_conn_info *conn, uint64_t wait_time
ssize_t result = 0; // closed
if (conn->fd > 0) {
int64_t remaining_time = 0;
uint64_t time_to_wait_to = get_absolute_time_in_ns();;
uint64_t time_to_wait_to = get_absolute_time_in_ns();
;
time_to_wait_to = time_to_wait_to + wait_time;
int flags = 1;
if (setsockopt(conn->fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags))) {
debug(1,"can't enable keepalive checking on the RTSP socket");
}
// remaining_time will be zero if wait_time is zero
if (wait_time != 0) {
remaining_time = time_to_wait_to - get_absolute_time_in_ns();
remaining_time = time_to_wait_to - get_absolute_time_in_ns();
}
do {
struct timeval tv;
@@ -1240,7 +1247,8 @@ ssize_t timed_read_from_rtsp_connection(rtsp_conn_info *conn, uint64_t wait_time
#ifdef CONFIG_AIRPLAY_2
if (conn->ap2_pairing_context.control_cipher_bundle.cipher_ctx) {
conn->ap2_pairing_context.control_cipher_bundle.is_encrypted = 1;
result = read_encrypted(conn->fd, &conn->ap2_pairing_context.control_cipher_bundle, buf, count);
result =
read_encrypted(conn->fd, &conn->ap2_pairing_context.control_cipher_bundle, buf, count);
} else {
result = read(conn->fd, buf, count);
}
@@ -1250,11 +1258,13 @@ ssize_t timed_read_from_rtsp_connection(rtsp_conn_info *conn, uint64_t wait_time
if (wait_time != 0)
remaining_time = time_to_wait_to - get_absolute_time_in_ns();
if (((result == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) && (remaining_time > 0))
debug(1,"remaining time on a timed read is %" PRId64 " ns.", remaining_time);
} while (((result == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) && (remaining_time > 0));
debug(1, "remaining time on a timed read is %" PRId64 " ns.", remaining_time);
} while (((result == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) &&
(remaining_time > 0));
} else {
debug(1,"Connection %d: attempt to read from a closed RTSP connection.",conn->connection_number);
debug(1, "Connection %d: attempt to read from a closed RTSP connection.",
conn->connection_number);
}
return result;
}
@@ -1268,9 +1278,7 @@ void set_client_as_ptp_clock(rtsp_conn_info *conn) {
ptp_send_control_message_string(timing_list_message);
}
void clear_ptp_clock() {
ptp_send_control_message_string("T");
}
void clear_ptp_clock() { ptp_send_control_message_string("T"); }
#endif
ssize_t read_from_rtsp_connection(rtsp_conn_info *conn, void *buf, size_t count) {
@@ -1333,7 +1341,7 @@ enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, rtsp_mes
while (msg_size < 0) {
if (conn->stop != 0) {
debug(3, "Connection %d: shutdown requested.", conn->connection_number);
debug(3, "Connection %d: Shutdown requested by client.", conn->connection_number);
reply = rtsp_read_request_response_immediate_shutdown_requested;
goto shutdown;
}
@@ -1341,16 +1349,14 @@ enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, rtsp_mes
nread = read_from_rtsp_connection(conn, buf + inbuf, buflen - inbuf);
if (nread == 0) {
// a blocking read that returns zero means eof -- implies connection closed
debug(1, "Connection %d: Closed by client: from %s:%u to self at %s:%u.",
conn->connection_number,
conn->client_ip_string, conn->client_rtsp_port,
conn->self_ip_string, conn->self_rtsp_port);
conn->fd = 0;
// a blocking read that returns zero means eof -- implies connection closed by client
debug(1, "Connection %d: Connection closed by client.", conn->connection_number);
reply = rtsp_read_request_response_channel_closed;
// Note: the socket will be closed when the thread exits
goto shutdown;
}
// An ETIMEDOUT error usually means keepalive has failed.
if (nread < 0) {
if (errno == EINTR)
@@ -1672,7 +1678,6 @@ void handle_record(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
#ifdef CONFIG_AIRPLAY_2
void handle_get_info(__attribute((unused)) rtsp_conn_info *conn, rtsp_message *req,
rtsp_message *resp) {
debug_log_rtsp_message(2, "GET /info:", req);
@@ -1894,8 +1899,8 @@ void handle_flushbuffered(rtsp_conn_info *conn, rtsp_message *req, rtsp_message
if ((conn->ap2_flush_requested != 0) && (conn->ap2_flush_from_valid != 0) &&
(flushFromValid != 0)) {
// if there is a request already, and it's a deferred request, and the current request is also
// deferred... do nothing! -- leave the starting point in place. Yeah, yeah, we know de Morgan's
// Law, but this seems clearer
// deferred... do nothing! -- leave the starting point in place. Yeah, yeah, we know de
// Morgan's Law, but this seems clearer
} else {
conn->ap2_flush_from_sequence_number = flushFromSeq;
conn->ap2_flush_from_rtp_timestamp = flushFromTS;
@@ -1915,7 +1920,7 @@ void handle_flushbuffered(rtsp_conn_info *conn, rtsp_message *req, rtsp_message
debug(2, "Deferred Flush Requested");
else
debug(2, "Immediate Flush Requested");
plist_free(messagePlist);
// display_all_flush_requests(conn);
}
@@ -2014,7 +2019,7 @@ void handle_setrateanchori(rtsp_conn_info *conn, rtsp_message *req, rtsp_message
#ifdef CONFIG_METADATA
send_ssnc_metadata('paus', NULL, 0, 1); // pause -- contains cancellation points
#endif
if (config.output->stop) {
if (config.output->stop) {
debug(2, "Connection %d: Stop the output backend.", conn->connection_number);
config.output->stop();
}
@@ -2570,38 +2575,38 @@ void handle_setpeers(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp
debug(2, "Connection %d: SETPEERS %s Content-Length %d", conn->connection_number, req->path,
req->contentlength);
debug_log_rtsp_message(2, "SETPEERS request", req);
/*
char timing_list_message[4096];
timing_list_message[0] = 'T';
timing_list_message[1] = 0;
/*
char timing_list_message[4096];
timing_list_message[0] = 'T';
timing_list_message[1] = 0;
// ensure the client itself is first -- it's okay if it's duplicated later
strncat(timing_list_message, " ", sizeof(timing_list_message) - 1 - strlen(timing_list_message));
strncat(timing_list_message, (const char *)&conn->client_ip_string,
sizeof(timing_list_message) - 1 - strlen(timing_list_message));
// ensure the client itself is first -- it's okay if it's duplicated later
strncat(timing_list_message, " ", sizeof(timing_list_message) - 1 -
strlen(timing_list_message)); strncat(timing_list_message, (const char
*)&conn->client_ip_string, sizeof(timing_list_message) - 1 - strlen(timing_list_message));
plist_t addresses_array = NULL;
plist_from_memory(req->content, req->contentlength, &addresses_array);
uint32_t items = plist_array_get_size(addresses_array);
if (items) {
uint32_t item;
for (item = 0; item < items; item++) {
plist_t n = plist_array_get_item(addresses_array, item);
char *ip_address = NULL;
plist_get_string_val(n, &ip_address);
// debug(1,ip_address);
strncat(timing_list_message, " ",
sizeof(timing_list_message) - 1 - strlen(timing_list_message));
strncat(timing_list_message, ip_address,
sizeof(timing_list_message) - 1 - strlen(timing_list_message));
if (ip_address != NULL)
free(ip_address);
plist_t addresses_array = NULL;
plist_from_memory(req->content, req->contentlength, &addresses_array);
uint32_t items = plist_array_get_size(addresses_array);
if (items) {
uint32_t item;
for (item = 0; item < items; item++) {
plist_t n = plist_array_get_item(addresses_array, item);
char *ip_address = NULL;
plist_get_string_val(n, &ip_address);
// debug(1,ip_address);
strncat(timing_list_message, " ",
sizeof(timing_list_message) - 1 - strlen(timing_list_message));
strncat(timing_list_message, ip_address,
sizeof(timing_list_message) - 1 - strlen(timing_list_message));
if (ip_address != NULL)
free(ip_address);
}
ptp_send_control_message_string(timing_list_message);
}
ptp_send_control_message_string(timing_list_message);
}
plist_free(addresses_array);
*/
//set_client_as_ptp_clock(conn);
plist_free(addresses_array);
*/
// set_client_as_ptp_clock(conn);
resp->respcode = 200;
}
#endif
@@ -2848,7 +2853,7 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
plist_t messagePlist = plist_from_rtsp_content(req);
plist_t setupResponsePlist = plist_new_dict();
resp->respcode = 400;
// see if we can get a name for the client
char *clientNameString = NULL;
plist_t nameItem = plist_dict_get_item(messagePlist, "name");
@@ -2867,6 +2872,7 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
"and open a TCP port.",
conn->connection_number);
conn->airplay_stream_category = unspecified_stream_category;
// figure out what category of stream it is, by looking at the plist
plist_t timingProtocol = plist_dict_get_item(messagePlist, "timingProtocol");
if (timingProtocol != NULL) {
@@ -2875,8 +2881,8 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
if (timingProtocolString) {
if (strcmp(timingProtocolString, "PTP") == 0) {
debug(1, "Connection %d: AP2 PTP connection from %s:%u (\"%s\") to self at %s:%u.",
conn->connection_number, conn->client_ip_string, conn->client_rtsp_port, clientNameString,
conn->self_ip_string, conn->self_rtsp_port);
conn->connection_number, conn->client_ip_string, conn->client_rtsp_port,
clientNameString, conn->self_ip_string, conn->self_rtsp_port);
conn->airplay_stream_category = ptp_stream;
conn->timing_type = ts_ptp;
#ifdef CONFIG_METADATA
@@ -2890,23 +2896,27 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
#endif
} else if (strcmp(timingProtocolString, "NTP") == 0) {
debug(1, "Connection %d: SETUP: NTP setup from %s:%u (\"%s\") to self at %s:%u.",
conn->connection_number, conn->client_ip_string, conn->client_rtsp_port, clientNameString,
conn->self_ip_string, conn->self_rtsp_port);
conn->connection_number, conn->client_ip_string, conn->client_rtsp_port,
clientNameString, conn->self_ip_string, conn->self_rtsp_port);
conn->airplay_stream_category = ntp_stream;
conn->timing_type = ts_ntp;
} else if (strcmp(timingProtocolString, "None") == 0) {
debug(3, "Connection %d: SETUP: a \"None\" setup detected from %s:%u (\"%s\") to self at %s:%u.",
conn->connection_number, conn->client_ip_string, conn->client_rtsp_port, clientNameString,
conn->self_ip_string, conn->self_rtsp_port);
debug(3,
"Connection %d: SETUP: a \"None\" setup detected from %s:%u (\"%s\") to self at "
"%s:%u.",
conn->connection_number, conn->client_ip_string, conn->client_rtsp_port,
clientNameString, conn->self_ip_string, conn->self_rtsp_port);
// now check to see if it's got the "isRemoteControlOnly" item and check it's true
plist_t isRemoteControlOnly = plist_dict_get_item(messagePlist, "isRemoteControlOnly");
if (isRemoteControlOnly != NULL) {
uint8_t isRemoteControlOnlyBoolean = 0;
plist_get_bool_val(isRemoteControlOnly, &isRemoteControlOnlyBoolean);
if (isRemoteControlOnlyBoolean != 0) {
debug(1, "Connection %d: Remote Control connection from %s:%u (\"%s\") to self at %s:%u.",
conn->connection_number, conn->client_ip_string, conn->client_rtsp_port, clientNameString,
conn->self_ip_string, conn->self_rtsp_port);
debug(
1,
"Connection %d: Remote Control connection from %s:%u (\"%s\") to self at %s:%u.",
conn->connection_number, conn->client_ip_string, conn->client_rtsp_port,
clientNameString, conn->self_ip_string, conn->self_rtsp_port);
conn->airplay_stream_category = remote_control_stream;
} else {
debug(1,
@@ -3039,7 +3049,7 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
freeifaddrs(addrs);
// debug(1,"initial timing peer command: \"%s\".", timing_list_message);
//ptp_send_control_message_string(timing_list_message);
// ptp_send_control_message_string(timing_list_message);
set_client_as_ptp_clock(conn);
plist_dict_set_item(timingPeerInfoPlist, "Addresses", addresses);
plist_dict_set_item(timingPeerInfoPlist, "ID", plist_new_string(conn->self_ip_string));
@@ -3160,7 +3170,7 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
// get the session key -- it must have one
plist_t item = plist_dict_get_item(stream0, "shk"); // session key
uint64_t item_value = 0; // the length
uint64_t item_value = 0; // the length
plist_get_data_val(item, (char **)&conn->session_key, &item_value);
// more stuff
@@ -3338,7 +3348,7 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
plist_array_append_item(streams_array, stream0dict);
plist_dict_set_item(setupResponsePlist, "streams", streams_array);
resp->respcode = 200;
} else if (conn->airplay_stream_category == remote_control_stream) {
debug(2, "Connection %d (RC): SETUP: Remote Control Stream received from %s.",
conn->connection_number, conn->client_ip_string);
@@ -3672,6 +3682,8 @@ void handle_set_parameter_parameter(rtsp_conn_info *conn, rtsp_message *req,
// play session is stopped.
// `svip` -- the payload is the IP number of the server, i.e. the player itself.
// Can be an IPv4 or an IPv6 number.
// `svna` -- the payload is the service name of the player, i.e. the name by
// which it is seen in the AirPlay menu.
// `disc` -- the payload is the IP number of the client, i.e. the sender of audio.
// Can be an IPv4 or an IPv6 number. This is an AirPlay-2-only message.
// It is sent when a client has been disconnected.
@@ -4452,8 +4464,8 @@ static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_messag
conn->airplay_type = ap_1;
conn->timing_type = ts_ntp;
debug(1, "Connection %d: Classic AirPlay connection from %s:%u to self at %s:%u.",
conn->connection_number, conn->client_ip_string, conn->client_rtsp_port,
conn->self_ip_string, conn->self_rtsp_port);
conn->connection_number, conn->client_ip_string, conn->client_rtsp_port,
conn->self_ip_string, conn->self_rtsp_port);
#endif
conn->stream.type = ast_unknown;
@@ -4547,7 +4559,7 @@ static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_messag
if ((paesiv == NULL) && (prsaaeskey == NULL)) {
// debug(1,"Unencrypted session requested?");
conn->stream.encrypted = 0;
} else if ((paesiv != NULL) && (prsaaeskey != NULL)){
} else if ((paesiv != NULL) && (prsaaeskey != NULL)) {
conn->stream.encrypted = 1;
// debug(1,"Encrypted session requested");
} else {
@@ -4751,7 +4763,7 @@ static void apple_challenge(int fd, rtsp_message *req, rtsp_message *resp) {
}
for (i = 0; i < 6; i++)
*bp++ = config.hw_addr[i];
*bp++ = config.ap1_prefix[i];
int buflen, resplen;
buflen = bp - buf;
@@ -4984,26 +4996,34 @@ void rtsp_conversation_thread_cleanup_function(void *arg) {
}
#endif
debug(3, "Connection %d terminating:Closing timing, control and audio sockets...",
debug(3, "Connection %d: terminating -- closing timing, control and audio sockets...",
conn->connection_number);
if (conn->control_socket) {
debug(3, "Connection %d: terminating -- closing control_socket %d.", conn->connection_number,
conn->control_socket);
close(conn->control_socket);
conn->control_socket = 0;
}
if (conn->timing_socket) {
debug(3, "Connection %d: terminating -- closing timing_socket %d.", conn->connection_number,
conn->timing_socket);
close(conn->timing_socket);
conn->timing_socket = 0;
}
if (conn->audio_socket) {
debug(3, "Connection %d: terminating -- closing audio_socket %d.", conn->connection_number,
conn->audio_socket);
close(conn->audio_socket);
conn->audio_socket = 0;
}
if (conn->fd > 0) {
debug(3, "Connection %d terminating: closing fd %d.", conn->connection_number, conn->fd);
close(conn->fd);
debug(3, "Connection %d terminating: closed fd %d.", conn->connection_number, conn->fd);
debug(1, "Connection %d Closed by self: from %s:%u to self at %s:%u.",
conn->connection_number, conn->client_ip_string, conn->client_rtsp_port,
debug(2,
"Connection %d: terminating -- closing RTSP connection socket %d: from %s:%u to self at "
"%s:%u.",
conn->connection_number, conn->fd, conn->client_ip_string, conn->client_rtsp_port,
conn->self_ip_string, conn->self_rtsp_port);
close(conn->fd);
conn->fd = 0;
}
if (conn->auth_nonce) {
free(conn->auth_nonce);
@@ -5475,6 +5495,41 @@ void *rtsp_listen_loop(__attribute((unused)) void *arg) {
} else {
size_of_reply = sizeof(SOCKADDR);
if (getsockname(conn->fd, (struct sockaddr *)&conn->local, &size_of_reply) == 0) {
// Thanks to https://holmeshe.me/network-essentials-setsockopt-SO_KEEPALIVE/ for this.
// turn on keepalive stuff -- wait for keepidle + (keepcnt * keepinttvl time) seconds before giving up
// an ETIMEOUT error is returned if the keepalive check fails
int keepAliveIdleTime = 35; // wait this many seconds before checking for a dropped client
int keepAliveCount = 5; // check this many times
int keepAliveInterval = 5; // wait this many seconds between checks
#if defined COMPILE_FOR_BSD || defined COMPILE_FOR_OSX
#define SOL_OPTION IPPROTO_TCP
#else
#define SOL_OPTION SOL_TCP
#endif
#ifdef COMPILE_FOR_OSX
#define KEEP_ALIVE_OR_IDLE_OPTION TCP_KEEPALIVE
#else
#define KEEP_ALIVE_OR_IDLE_OPTION TCP_KEEPIDLE
#endif
if (setsockopt(conn->fd, SOL_OPTION, KEEP_ALIVE_OR_IDLE_OPTION, (void *)&keepAliveIdleTime, sizeof(keepAliveIdleTime))) {
debug(1,"can't set the keepidle wait time");
}
if (setsockopt(conn->fd, SOL_OPTION, TCP_KEEPCNT, (void *)&keepAliveCount, sizeof(keepAliveCount))) {
debug(1,"can't set the keepidle missing count");
}
if (setsockopt(conn->fd, SOL_OPTION , TCP_KEEPINTVL, (void *)&keepAliveInterval, sizeof(keepAliveInterval))) {
debug(1,"can't set the keepidle missing count interval");
};
// initialise the connection info
void *client_addr = NULL, *self_addr = NULL;
conn->connection_ip_family = conn->local.SAFAMILY;
+3
View File
@@ -216,6 +216,9 @@ metadata =
// cover_art_cache_directory = "/tmp/shairport-sync/.cache/coverart"; // artwork will be stored in this directory if the dbus or MPRIS interfaces are enabled or if the MQTT client is in use. Set it to "" to prevent caching, which may be useful on some systems
// pipe_name = "/tmp/shairport-sync-metadata";
// pipe_timeout = 5000; // wait for this number of milliseconds for a blocked pipe to unblock before giving up
// progress_interval = 0.0; // if non-zero, progress 'phbt' messages will be sent at the interval specified in seconds. A 'phb0' message will also be sent when the first audio frame of a play session is about to be played.
// Each message consists of the RTPtime of a a frame of audio and the exact system time when it is to be played. The system time, in nanoseconds, is based the CLOCK_MONOTONIC_RAW of the machine -- if available -- or CLOCK_MONOTONIC otherwise.
// Messages are sent when the frame is placed in the output device's buffer, thus, they will be _approximately_ 'audio_backend_buffer_desired_length_in_seconds' (default 0.2 seconds) ahead of time.
// socket_address = "226.0.0.1"; // if set to a host name or IP address, UDP packets containing metadata will be sent to this address. May be a multicast address. "socket-port" must be non-zero and "enabled" must be set to yes"
// socket_port = 5555; // if socket_address is set, the port to send UDP packets to
// socket_msglength = 65000; // the maximum packet size for any UDP metadata. This will be clipped to be between 500 or 65000. The default is 500.
+48 -26
View File
@@ -999,6 +999,10 @@ int parse_options(int argc, char **argv) {
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;
}
@@ -1596,10 +1600,10 @@ void exit_function() {
#ifdef CONFIG_SOXR
// be careful -- not sure if the thread can be cancelled cleanly, so wait for it to shut down
if (soxr_time_check_thread_started != 0) {
debug(1, "Waiting for SoXr timecheck to terminate...");
debug(2, "Waiting for SoXr timecheck to terminate...");
pthread_join(soxr_time_check_thread, NULL);
soxr_time_check_thread_started = 0;
debug(1, "Waiting for SoXr timecheck to terminate done");
debug(2, "Waiting for SoXr timecheck to terminate done");
}
#endif
@@ -2454,34 +2458,43 @@ int main(int argc, char **argv) {
soxr_time_check_thread_started = 1;
#endif
/*
uint8_t ap_md5[16];
// calculate the 12-hex-digit prefix by hashing the service name.
uint8_t ap_md5[16];
#ifdef CONFIG_OPENSSL
MD5_CTX ctx;
MD5_Init(&ctx);
MD5_Update(&ctx, config.service_name, strlen(config.service_name));
MD5_Final(ap_md5, &ctx);
#endif
debug(1, "size of hw_addr is %u.", sizeof(config.hw_addr));
#ifdef CONFIG_OPENSSL
MD5_CTX ctx;
MD5_Init(&ctx);
MD5_Update(&ctx, config.service_name, strlen(config.service_name));
MD5_Update(&ctx, config.hw_addr, sizeof(config.hw_addr));
MD5_Final(ap_md5, &ctx);
#endif
#ifdef CONFIG_MBEDTLS
#if MBEDTLS_VERSION_MINOR >= 7
mbedtls_md5_context tctx;
mbedtls_md5_starts_ret(&tctx);
mbedtls_md5_update_ret(&tctx, (unsigned char *)config.service_name,
strlen(config.service_name)); mbedtls_md5_finish_ret(&tctx, ap_md5); #else mbedtls_md5_context
tctx; mbedtls_md5_starts(&tctx); mbedtls_md5_update(&tctx, (unsigned char *)config.service_name,
strlen(config.service_name)); mbedtls_md5_finish(&tctx, ap_md5); #endif #endif
#ifdef CONFIG_MBEDTLS
#if MBEDTLS_VERSION_MINOR >= 7
mbedtls_md5_context tctx;
mbedtls_md5_starts_ret(&tctx);
mbedtls_md5_update_ret(&tctx, (unsigned char *)config.service_name, strlen(config.service_name));
mbedtls_md5_update_ret(&tctx, (unsigned char *)config.hw_addr, sizeof(config.hw_addr));
mbedtls_md5_finish_ret(&tctx, ap_md5);
#else
mbedtls_md5_context tctx;
mbedtls_md5_starts(&tctx);
mbedtls_md5_update(&tctx, (unsigned char *)config.service_name, strlen(config.service_name));
mbedtls_md5_update(&tctx, (unsigned char *)config.hw_addr, sizeof(config.hw_addr));
mbedtls_md5_finish(&tctx, ap_md5);
#endif
#endif
#ifdef CONFIG_POLARSSL
md5_context tctx;
md5_starts(&tctx);
md5_update(&tctx, (unsigned char *)config.service_name, strlen(config.service_name));
md5_finish(&tctx, ap_md5);
#endif
#ifdef CONFIG_POLARSSL
md5_context tctx;
md5_starts(&tctx);
md5_update(&tctx, (unsigned char *)config.service_name, strlen(config.service_name));
md5_update(&tctx, (unsigned char *)config.hw_addr, sizeof(config.hw_addr));
md5_finish(&tctx, ap_md5);
#endif
memcpy(config.hw_addr, ap_md5, sizeof(config.hw_addr));
*/
memcpy(config.ap1_prefix, ap_md5, sizeof(config.ap1_prefix));
#ifdef CONFIG_METADATA
metadata_init(); // create the metadata pipe if necessary
@@ -2537,6 +2550,15 @@ int main(int argc, char **argv) {
}
#endif
#ifdef CONFIG_METADATA
send_ssnc_metadata('svna', config.service_name, strlen(config.service_name), 1);
char buffer[256] = "";
snprintf(buffer, sizeof(buffer), "%d", config.output_rate);
send_ssnc_metadata('ofps', buffer, strlen(buffer), 1);
snprintf(buffer, sizeof(buffer), "%s", sps_format_description_string(config.output_format));
send_ssnc_metadata('ofmt', buffer, strlen(buffer), 1);
#endif
activity_monitor_start(); // not yet for AP2
pthread_create(&rtsp_listener_thread, NULL, &rtsp_listen_loop, NULL);
atexit(exit_rtsp_listener);