]> git.ipfire.org Git - thirdparty/shairport-sync.git/commitdiff
Seems to work better with iOS / HomePod mini speaker selection. Does not recover...
authorMike Brady <4265913+mikebrady@users.noreply.github.com>
Mon, 6 Sep 2021 08:11:24 +0000 (09:11 +0100)
committerMike Brady <4265913+mikebrady@users.noreply.github.com>
Mon, 6 Sep 2021 08:11:24 +0000 (09:11 +0100)
player.h
rtsp.c

index d980f1c2ed9dca4030545daa875c71d8460b30d9..64e9e3cb620211f1c0d84c2caafaa60347827fb9 100644 (file)
--- a/player.h
+++ b/player.h
@@ -105,7 +105,7 @@ typedef struct {
 typedef enum { ts_ntp, ts_ptp } timing_t;
 typedef enum { ap_1, ap_2 } airplay_t;
 typedef enum {
-  unknown_stream_category,
+  unspecified_stream_category = 0,
   ptp_stream,
   ntp_stream,
   remote_control_stream
@@ -144,6 +144,7 @@ typedef struct flush_request_t {
 typedef struct {
   int connection_number;     // for debug ID purposes, nothing else...
   int resend_interval;       // this is really just for debugging
+  int has_been_cancelled;    // for cleaning up later.
   char *UserAgent;           // free this on teardown
   int AirPlayVersion;        // zero if not an AirPlay session. Used to help calculate latency
   uint32_t latency;          // the actual latency used for this play session
diff --git a/rtsp.c b/rtsp.c
index 3ef385da9d9bd6e030a280ce7b127fa3f71bdf23..7d4e5ecd0b27541ed68b3fc96cbedddb96cd5373 100644 (file)
--- a/rtsp.c
+++ b/rtsp.c
@@ -624,20 +624,30 @@ static void track_thread(rtsp_conn_info *conn) {
   }
 }
 
-void cancel_all_RTSP_threads(void) {
+// note: connection numbers start at 1, so an except_this_one value of zero means "all threads"
+void cancel_all_RTSP_threads(airplay_stream_c stream_category, int except_this_one) {
+  // if the stream category is unspecified_stream_category
+  // all categories are elegible for cancellation
+  // otherwise just the category itself
+
   int i;
   for (i = 0; i < nconns; i++) {
-    debug(2, "Connection %d: cancelling.", conns[i]->connection_number);
-    pthread_cancel(conns[i]->thread);
+    if ((conns[i]->has_been_cancelled == 0) && (conns[i]->connection_number != except_this_one) && ((stream_category == unspecified_stream_category) || (stream_category == conns[i]->airplay_stream_category))) {
+      debug(2, "Connection %d: cancelling.", conns[i]->connection_number);
+      pthread_cancel(conns[i]->thread);
+    }
   }
   for (i = 0; i < nconns; i++) {
-    debug(2, "Connection %d: joining.", conns[i]->connection_number);
-    pthread_join(conns[i]->thread, NULL);
-    free(conns[i]);
+    if ((conns[i]->has_been_cancelled == 0) && (conns[i]->connection_number != except_this_one) && ((stream_category == unspecified_stream_category) || (stream_category == conns[i]->airplay_stream_category))) {
+      debug(2, "Connection %d: joining.", conns[i]->connection_number);
+      pthread_join(conns[i]->thread, NULL);
+      conns[i]->has_been_cancelled = 1;
+    }
   }
 }
 
 void cleanup_threads(void) {
+  /*
   void *retval;
   int i;
   // debug(2, "culling threads.");
@@ -655,6 +665,7 @@ void cleanup_threads(void) {
       i++;
     }
   }
+  */
 }
 
 // park a null at the line ending, and return the next line pointer
@@ -1350,9 +1361,31 @@ int msg_write_response(rtsp_conn_info *conn, rtsp_message *resp) {
   return 0;
 }
 
+char *get_category_string(airplay_stream_c cat) {
+  char *category;
+  switch (cat) {
+    case unspecified_stream_category:
+      category = "unspecified stream";
+      break;
+    case ptp_stream:
+      category = "PTP stream";
+      break;
+    case ntp_stream:
+      category = "NTP stream";
+      break;
+    case remote_control_stream:
+      category = "Remote Control stream";
+      break;
+    default:
+       category = "Unexpected stream code";
+      break;
+  }
+  return category;
+}
+
 void handle_record_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
-  debug(2, "Connection %d: RECORD (AirPlay 2)", conn->connection_number);
-  debug_log_rtsp_message(2, "RECORD incoming message", req);
+  debug(1, "Connection %d: RECORD on %s", conn->connection_number, get_category_string(conn->airplay_stream_category));
+  //debug_log_rtsp_message(1, "RECORD incoming message", req);
   resp->respcode = 200;
 }
 
@@ -1417,7 +1450,7 @@ void handle_get_info(__attribute((unused)) rtsp_conn_info *conn, rtsp_message *r
         hdr = hdr + strlen("AirPlay/");
         // double airplay_version = 0.0;
         // airplay_version = atof(hdr);
-        debug(1, "Connection %d: GET_INFO: Source AirPlay Version is: %s.", conn->connection_number,
+        debug(2, "Connection %d: GET_INFO: Source AirPlay Version is: %s.", conn->connection_number,
               hdr);
       }
     }
@@ -2241,7 +2274,7 @@ void handle_options(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message *
 void handle_teardown_2(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message *req,
                        rtsp_message *resp) {
 
-  // debug_log_rtsp_message(1, "TEARDOWN: ", req);
+  debug_log_rtsp_message(2, "TEARDOWN: ", req);
   // if (have_player(conn)) {
   resp->respcode = 200;
   msg_add_header(resp, "Connection", "close");
@@ -2252,7 +2285,7 @@ void handle_teardown_2(rtsp_conn_info *conn, __attribute__((unused)) rtsp_messag
     plist_t streams = plist_dict_get_item(messagePlist, "streams");
 
     if (streams) {
-      debug(1, "Connection %d: TEARDOWN a stream.", conn->connection_number);
+      debug(1, "Connection %d: TEARDOWN a %s.", conn->connection_number, get_category_string(conn->airplay_stream_category));
       // we are being asked to close a stream
       player_stop(conn);
       activity_monitor_signify_activity(0); // inactive, and should be after command_stop()
@@ -2264,22 +2297,26 @@ void handle_teardown_2(rtsp_conn_info *conn, __attribute__((unused)) rtsp_messag
       debug(2, "Connection %d: Stream TEARDOWN complete", conn->connection_number);
     } else {
       // we are being asked to disconnect
-      debug(1, "Connection %d: TEARDOWN a connection.", conn->connection_number);
+      debug(1, "Connection %d: TEARDOWN a %s connection.", conn->connection_number, get_category_string(conn->airplay_stream_category));
       debug(2, "Connection %d: TEARDOWN Delete Event Thread.", conn->connection_number);
       pthread_cancel(conn->rtp_event_thread);
       pthread_join(conn->rtp_event_thread, NULL);
       debug(2, "Connection %d: TEARDOWN Close Event Socket.", conn->connection_number);
       if (conn->event_socket)
         close(conn->event_socket);
-      if (conn->airplay_gid != NULL) {
-        free(conn->airplay_gid);
-        conn->airplay_gid = NULL;
+
+      // if we are closing a PTP stream only, do this
+      if (conn->airplay_stream_category == ptp_stream) {
+        if (conn->airplay_gid != NULL) {
+          free(conn->airplay_gid);
+          conn->airplay_gid = NULL;
+        }
+        conn->groupContainsGroupLeader = 0;
+        config.airplay_statusflags &= (0xffffffff - (1 << 11)); // DeviceSupportsRelay
+        build_bonjour_strings(conn);
+        debug(1, "Connection %d: TEARDOWN mdns_update on %s.", conn->connection_number, get_category_string(conn->airplay_stream_category));
+        mdns_update(NULL, secondary_txt_records);
       }
-      conn->groupContainsGroupLeader = 0;
-      config.airplay_statusflags &= (0xffffffff - (1 << 11)); // DeviceSupportsRelay
-      build_bonjour_strings(conn);
-      debug(1, "Connection %d: TEARDOWN mdns_update.", conn->connection_number);
-      mdns_update(NULL, secondary_txt_records);
       debug(2, "Connection %d: non-stream TEARDOWN complete", conn->connection_number);
     }
     //} else {
@@ -2374,44 +2411,44 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
   if (streams == NULL) {
     // no "streams" plist, so it must (?) be an initial setup
     debug(2,
-          "SETUP on Connection %d: No \"streams\" array has been found -- create an event thread "
+          "Connection %d SETUP: No \"streams\" array has been found -- create an event thread "
           "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
-    conn->airplay_stream_category = unknown_stream_category;
     plist_t timingProtocol = plist_dict_get_item(messagePlist, "timingProtocol");
     if (timingProtocol != NULL) {
       char *timingProtocolString = NULL;
       plist_get_string_val(timingProtocol, &timingProtocolString);
       if (timingProtocolString) {
         if (strcmp(timingProtocolString, "PTP") == 0) {
-          debug(1, "SETUP on Connection %d: PTP setup detected.", conn->connection_number);
+          debug(1, "Connection %d: SETUP: PTP setup detected.", conn->connection_number);
           conn->airplay_stream_category = ptp_stream;
           conn->timing_type = ts_ptp;
         } else if (strcmp(timingProtocolString, "NTP") == 0) {
-          debug(1, "SETUP on Connection %d: NTP setup detected.", conn->connection_number);
+          debug(1, "Connection %d: SETUP: NTP setup detected.", conn->connection_number);
           conn->airplay_stream_category = ntp_stream;
           conn->timing_type = ts_ntp;
         } else if (strcmp(timingProtocolString, "None") == 0) {
-          debug(2, "SETUP on Connection %d: a \"None\" setup detected.", conn->connection_number);
+          debug(2, "Connection %d: SETUP: a \"None\" setup detected.", conn->connection_number);
           // 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, "SETUP on Connection %d: Remote Control setup detected.",
+              debug(1, "Connection %d: SETUP: Remote Control setup detected.",
                     conn->connection_number);
               conn->airplay_stream_category = remote_control_stream;
             } else {
               debug(1,
-                    "SETUP on Connection %d: a \"None\" setup detected, with "
+                    "Connection %d: SETUP: a \"None\" setup detected, with "
                     "\"isRemoteControlOnly\" item set to \"false\".",
                     conn->connection_number);
             }
           } else {
             debug(1,
-                  "SETUP on Connection %d: a \"None\" setup detected, but no "
+                  "Connection %d: SETUP: a \"None\" setup detected, but no "
                   "\"isRemoteControlOnly\" item detected.",
                   conn->connection_number);
           }
@@ -2558,6 +2595,13 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
             plist_dict_set_item(setupResponsePlist, "eventPort",
                                 plist_new_uint(conn->local_event_port));
             plist_dict_set_item(setupResponsePlist, "timingPort", plist_new_uint(0)); // dummy
+            cancel_all_RTSP_threads(unspecified_stream_category, conn->connection_number); // kill all the other listeners
+
+            config.airplay_statusflags |= 1 << 11; // DeviceSupportsRelay
+            build_bonjour_strings(conn);
+            debug(1, "Connection %d: SETUP mdns_update on %s.", conn->connection_number, get_category_string(conn->airplay_stream_category));
+            mdns_update(NULL, secondary_txt_records);
+
             resp->respcode = 200;
           } else {
             debug(1, "SETUP on Connection %d: PTP setup -- no timingPeerInfo plist.",
@@ -2568,8 +2612,26 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
                 conn->connection_number, req);
           warn("Shairport Sync can not handle NTP streams.");
         } else if (conn->airplay_stream_category == remote_control_stream) {
-          debug_log_rtsp_message(1, "SETUP \"isRemoteControlOnly\" message", req);
-          // resp->respcode = 200;
+          debug_log_rtsp_message(2, "SETUP \"isRemoteControlOnly\" message", req);
+
+          // get a port to use as an event port
+          // bind a new TCP port and get a socket
+          conn->local_event_port = 0; // any port
+          int err = bind_socket_and_port(SOCK_STREAM, conn->connection_ip_family,
+                                         conn->self_ip_string, conn->self_scope_id,
+                                         &conn->local_event_port, &conn->event_socket);
+          if (err) {
+            die("SETUP on Connection %d: Error %d: could not find a TCP port to use as an event "
+                "port",
+                conn->connection_number, err);
+          }
+
+          pthread_create(&conn->rtp_event_thread, NULL, &rtp_event_receiver, (void *)conn);
+
+          plist_dict_set_item(setupResponsePlist, "eventPort",
+                              plist_new_uint(conn->local_event_port));
+          cancel_all_RTSP_threads(remote_control_stream, conn->connection_number); // kill all the other remote control listeners
+          resp->respcode = 200;
         } else {
           debug(1, "SETUP on Connection %d: an unrecognised \"%s\" setup detected.",
                 conn->connection_number, timingProtocolString);
@@ -2590,9 +2652,8 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
     }
   } else {
     debug(2,
-          "Connection %d: SETUP. A \"streams\" array has been found -- create control and audio "
-          "threads and ports",
-          conn->connection_number);
+          "Connection %d: SETUP on %s. A \"streams\" array has been found",
+          conn->connection_number, get_category_string(conn->airplay_stream_category));
     if (conn->airplay_stream_category == ptp_stream) {
       // get stream[0]
       plist_t stream0 = plist_array_get_item(streams, 0);
@@ -2779,11 +2840,11 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
       plist_dict_set_item(setupResponsePlist, "streams", streams_array);
       resp->respcode = 200;
     } else if (conn->airplay_stream_category == remote_control_stream) {
-      debug(1, "SETUP on Connection %d: Remote Control Stream received. Nothing done.",
+      debug(1, "Connection %d: SETUP: Remote Control Stream received.",
             conn->connection_number);
-      // resp->respcode = 200;
+      resp->respcode = 200;
     } else {
-      debug(1, "SETUP on Connection %d: Stream received but no airplay category set. Nothing done.",
+      debug(1, "Connection %d: SETUP: Stream received but no airplay category set. Nothing done.",
             conn->connection_number);
     }
   }
@@ -2792,10 +2853,6 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
     plist_to_bin(setupResponsePlist, &resp->content, &resp->contentlength);
     plist_free(setupResponsePlist);
     msg_add_header(resp, "Content-Type", "application/x-apple-binary-plist");
-    // config.airplay_statusflags |= 1 << 11; // DeviceSupportsRelay
-    build_bonjour_strings(conn);
-    debug(1, "Connection %d: SETUP mdns_update.", conn->connection_number);
-    mdns_update(NULL, secondary_txt_records);
   }
   debug_log_rtsp_message(2, " SETUP response", resp);
 }
@@ -2808,7 +2865,7 @@ void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
     uint16_t cport, tport;
     char *ar = msg_get_header(req, "Active-Remote");
     if (ar) {
-      debug(2, "Connection %d: SETUP -- Active-Remote string seen: \"%s\".",
+      debug(2, "Connection %d: SETUP: Active-Remote string seen: \"%s\".",
             conn->connection_number, ar);
       // get the active remote
       if (conn->dacp_active_remote) // this is in case SETUP was previously called
@@ -2818,7 +2875,7 @@ void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
       send_metadata('ssnc', 'acre', ar, strlen(ar), req, 1);
 #endif
     } else {
-      debug(2, "Connection %d: SETUP -- Note: no Active-Remote information  the SETUP Record.",
+      debug(2, "Connection %d: SETUP: Note: no Active-Remote information seen.",
             conn->connection_number);
       if (conn->dacp_active_remote) { // this is in case SETUP was previously called
         free(conn->dacp_active_remote);
@@ -2828,7 +2885,7 @@ void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
 
     ar = msg_get_header(req, "DACP-ID");
     if (ar) {
-      debug(2, "Connection %d: SETUP -- DACP-ID string seen: \"%s\".", conn->connection_number, ar);
+      debug(2, "Connection %d: SETUP: DACP-ID string seen: \"%s\".", conn->connection_number, ar);
       if (conn->dacp_id) // this is in case SETUP was previously called
         free(conn->dacp_id);
       conn->dacp_id = strdup(ar);
@@ -4645,7 +4702,7 @@ void rtsp_listen_loop_cleanup_handler(__attribute__((unused)) void *arg) {
   int oldState;
   pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
   debug(2, "rtsp_listen_loop_cleanup_handler called.");
-  cancel_all_RTSP_threads();
+  cancel_all_RTSP_threads(unspecified_stream_category,0); // kill all RTSP listeners
   int *sockfd = (int *)arg;
   mdns_unregister();
   if (sockfd) {