diff --git a/player.c b/player.c index 7560bfb4..c3b74267 100644 --- a/player.c +++ b/player.c @@ -1659,8 +1659,6 @@ static void *player_thread_func(void *arg) { player_volume(config.airplay_volume, conn); int64_t frames_to_drop = 0; // debug(1, "Play begin"); - if (play_number % 100 == 0) - debug(3, "Play frame %d.", play_number); while (!conn->player_thread_please_stop) { abuf_t *inframe = buffer_get_frame(conn); if (inframe) { @@ -1668,9 +1666,11 @@ static void *player_thread_func(void *arg) { inbuflength = inframe->length; if (inbuf) { play_number++; + if (play_number % 100 == 0) + debug(3, "Play frame %d.", play_number); conn->play_number_after_flush++; if (inframe->timestamp == 0) { - // debug(1,"Player has a supplied silent frame."); + debug(3, "Player has a supplied silent frame."); conn->last_seqno_read = (SUCCESSOR(conn->last_seqno_read) & 0xffff); // manage the packet out of sequence minder config.output->play(silence, conn->max_frames_per_packet * conn->output_sample_ratio); @@ -1685,7 +1685,9 @@ static void *player_thread_func(void *arg) { */ config.output->play(silence, conn->max_frames_per_packet * conn->output_sample_ratio); } else if (frames_to_drop) { - // debug(2,"%lld frames to drop.",frames_to_drop); + if (frames_to_drop > 3 * config.output_rate) + warn("Very large number of frames to drop: %" PRId64 "."); + debug(3, "%" PRId64 " frames to drop.", frames_to_drop); frames_to_drop -= inframe->length; if (frames_to_drop < 0) frames_to_drop = 0; @@ -1885,6 +1887,9 @@ static void *player_thread_func(void *arg) { if ((config.no_sync == 0) && (inframe->timestamp != 0) && (!conn->player_thread_please_stop) && (config.resyncthreshold > 0.0) && (abs_sync_error > config.resyncthreshold * config.output_rate)) { + if (abs_sync_error > 3 * config.output_rate) { + warn("The sync error is very large: %" PRId64 " frames."); + } sync_error_out_of_bounds++; } else { sync_error_out_of_bounds = 0; diff --git a/player.h b/player.h index 4ceabf9c..6f1a8b5b 100644 --- a/player.h +++ b/player.h @@ -141,6 +141,12 @@ typedef struct { int control_socket; // our local [server] control socket int timing_socket; // local timing socket + uint16_t remote_control_port; + uint16_t remote_timing_port; + uint16_t local_audio_port; + uint16_t local_control_port; + uint16_t local_timing_port; + int64_t latency_delayed_timestamp; // this is for debugging only... int64_t reference_timestamp; uint64_t reference_timestamp_time; diff --git a/rtp.c b/rtp.c index 5b88c3af..8168de14 100644 --- a/rtp.c +++ b/rtp.c @@ -691,9 +691,10 @@ void *rtp_timing_receiver(void *arg) { return NULL; } -static int bind_port(int ip_family, const char *self_ip_address, uint32_t scope_id, int *sock) { +static uint16_t bind_port(int ip_family, const char *self_ip_address, uint32_t scope_id, + int *sock) { // look for a port in the range, if any was specified. - int desired_port = config.udp_port_base; + uint16_t desired_port = config.udp_port_base; int ret; int local_socket = socket(ip_family, SOCK_DGRAM, IPPROTO_UDP); @@ -731,7 +732,7 @@ static int bind_port(int ip_family, const char *self_ip_address, uint32_t scope_ "check for restrictive firewall settings or a bad router!"); } - int sport; + uint16_t sport; SOCKADDR local; socklen_t local_len = sizeof(local); getsockname(local_socket, (struct sockaddr *)&local, &local_len); @@ -751,123 +752,128 @@ static int bind_port(int ip_family, const char *self_ip_address, uint32_t scope_ return sport; } -void rtp_setup(SOCKADDR *local, SOCKADDR *remote, int cport, int tport, int *lsport, int *lcport, - int *ltport, rtsp_conn_info *conn) { +void rtp_setup(SOCKADDR *local, SOCKADDR *remote, uint16_t cport, uint16_t tport, + rtsp_conn_info *conn) { // this gets the local and remote ip numbers (and ports used for the TCD stuff) // we use the local stuff to specify the address we are coming from and // we use the remote stuff to specify where we're goint to if (conn->rtp_running) - die("rtp_setup called with active stream!"); + warn("rtp_setup has been called with al already-active stream -- ignored. Possible duplicate " + "SETUP call?"); + else { - debug(2, "rtp_setup: cport=%d tport=%d.", cport, tport); + debug(2, "rtp_setup: cport=%d tport=%d.", cport, tport); - // print out what we know about the client - void *client_addr = NULL, *self_addr = NULL; - // int client_port, self_port; - // char client_port_str[64]; - // char self_addr_str[64]; + // print out what we know about the client + void *client_addr = NULL, *self_addr = NULL; + // int client_port, self_port; + // char client_port_str[64]; + // char self_addr_str[64]; - conn->connection_ip_family = - remote->SAFAMILY; // keep information about the kind of ip of the client + conn->connection_ip_family = + remote->SAFAMILY; // keep information about the kind of ip of the client #ifdef AF_INET6 - if (conn->connection_ip_family == AF_INET6) { - struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)remote; - client_addr = &(sa6->sin6_addr); - // client_port = ntohs(sa6->sin6_port); - sa6 = (struct sockaddr_in6 *)local; - self_addr = &(sa6->sin6_addr); - // self_port = ntohs(sa6->sin6_port); - conn->self_scope_id = sa6->sin6_scope_id; - } + if (conn->connection_ip_family == AF_INET6) { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)remote; + client_addr = &(sa6->sin6_addr); + // client_port = ntohs(sa6->sin6_port); + sa6 = (struct sockaddr_in6 *)local; + self_addr = &(sa6->sin6_addr); + // self_port = ntohs(sa6->sin6_port); + conn->self_scope_id = sa6->sin6_scope_id; + } #endif - if (conn->connection_ip_family == AF_INET) { - struct sockaddr_in *sa4 = (struct sockaddr_in *)remote; - client_addr = &(sa4->sin_addr); - // client_port = ntohs(sa4->sin_port); - sa4 = (struct sockaddr_in *)local; - self_addr = &(sa4->sin_addr); - // self_port = ntohs(sa4->sin_port); - } + if (conn->connection_ip_family == AF_INET) { + struct sockaddr_in *sa4 = (struct sockaddr_in *)remote; + client_addr = &(sa4->sin_addr); + // client_port = ntohs(sa4->sin_port); + sa4 = (struct sockaddr_in *)local; + self_addr = &(sa4->sin_addr); + // self_port = ntohs(sa4->sin_port); + } - inet_ntop(conn->connection_ip_family, client_addr, conn->client_ip_string, - sizeof(conn->client_ip_string)); - inet_ntop(conn->connection_ip_family, self_addr, conn->self_ip_string, - sizeof(conn->self_ip_string)); + inet_ntop(conn->connection_ip_family, client_addr, conn->client_ip_string, + sizeof(conn->client_ip_string)); + inet_ntop(conn->connection_ip_family, self_addr, conn->self_ip_string, + sizeof(conn->self_ip_string)); - debug(2, "Set up play connection from %s to self at %s on RTSP conversation thread %d.", - conn->client_ip_string, conn->self_ip_string, conn->connection_number); + debug(2, "Set up play connection from %s to self at %s on RTSP conversation thread %d.", + conn->client_ip_string, conn->self_ip_string, conn->connection_number); - // set up a the record of the remote's control socket - struct addrinfo hints; - struct addrinfo *servinfo; + // set up a the record of the remote's control socket + struct addrinfo hints; + struct addrinfo *servinfo; - memset(&conn->rtp_client_control_socket, 0, sizeof(conn->rtp_client_control_socket)); - memset(&hints, 0, sizeof hints); - hints.ai_family = conn->connection_ip_family; - hints.ai_socktype = SOCK_DGRAM; - char portstr[20]; - snprintf(portstr, 20, "%d", cport); - if (getaddrinfo(conn->client_ip_string, portstr, &hints, &servinfo) != 0) - die("Can't get address of client's control port"); + memset(&conn->rtp_client_control_socket, 0, sizeof(conn->rtp_client_control_socket)); + memset(&hints, 0, sizeof hints); + hints.ai_family = conn->connection_ip_family; + hints.ai_socktype = SOCK_DGRAM; + char portstr[20]; + snprintf(portstr, 20, "%d", cport); + if (getaddrinfo(conn->client_ip_string, portstr, &hints, &servinfo) != 0) + die("Can't get address of client's control port"); #ifdef AF_INET6 - if (servinfo->ai_family == AF_INET6) { - memcpy(&conn->rtp_client_control_socket, servinfo->ai_addr, sizeof(struct sockaddr_in6)); - // ensure the scope id matches that of remote. this is needed for link-local addresses. - struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&conn->rtp_client_control_socket; - sa6->sin6_scope_id = conn->self_scope_id; - } else + if (servinfo->ai_family == AF_INET6) { + memcpy(&conn->rtp_client_control_socket, servinfo->ai_addr, sizeof(struct sockaddr_in6)); + // ensure the scope id matches that of remote. this is needed for link-local addresses. + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&conn->rtp_client_control_socket; + sa6->sin6_scope_id = conn->self_scope_id; + } else #endif - memcpy(&conn->rtp_client_control_socket, servinfo->ai_addr, sizeof(struct sockaddr_in)); - freeaddrinfo(servinfo); + memcpy(&conn->rtp_client_control_socket, servinfo->ai_addr, sizeof(struct sockaddr_in)); + freeaddrinfo(servinfo); - // set up a the record of the remote's timing socket - memset(&conn->rtp_client_timing_socket, 0, sizeof(conn->rtp_client_timing_socket)); - memset(&hints, 0, sizeof hints); - hints.ai_family = conn->connection_ip_family; - hints.ai_socktype = SOCK_DGRAM; - snprintf(portstr, 20, "%d", tport); - if (getaddrinfo(conn->client_ip_string, portstr, &hints, &servinfo) != 0) - die("Can't get address of client's timing port"); + // set up a the record of the remote's timing socket + memset(&conn->rtp_client_timing_socket, 0, sizeof(conn->rtp_client_timing_socket)); + memset(&hints, 0, sizeof hints); + hints.ai_family = conn->connection_ip_family; + hints.ai_socktype = SOCK_DGRAM; + snprintf(portstr, 20, "%d", tport); + if (getaddrinfo(conn->client_ip_string, portstr, &hints, &servinfo) != 0) + die("Can't get address of client's timing port"); #ifdef AF_INET6 - if (servinfo->ai_family == AF_INET6) { - memcpy(&conn->rtp_client_timing_socket, servinfo->ai_addr, sizeof(struct sockaddr_in6)); - // ensure the scope id matches that of remote. this is needed for link-local addresses. - struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&conn->rtp_client_timing_socket; - sa6->sin6_scope_id = conn->self_scope_id; - } else + if (servinfo->ai_family == AF_INET6) { + memcpy(&conn->rtp_client_timing_socket, servinfo->ai_addr, sizeof(struct sockaddr_in6)); + // ensure the scope id matches that of remote. this is needed for link-local addresses. + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&conn->rtp_client_timing_socket; + sa6->sin6_scope_id = conn->self_scope_id; + } else #endif - memcpy(&conn->rtp_client_timing_socket, servinfo->ai_addr, sizeof(struct sockaddr_in)); - freeaddrinfo(servinfo); + memcpy(&conn->rtp_client_timing_socket, servinfo->ai_addr, sizeof(struct sockaddr_in)); + freeaddrinfo(servinfo); - // now, we open three sockets -- one for the audio stream, one for the timing and one for the - // control + // now, we open three sockets -- one for the audio stream, one for the timing and one for the + // control + conn->remote_control_port = cport; + conn->remote_timing_port = tport; - *lsport = bind_port(conn->connection_ip_family, conn->self_ip_string, conn->self_scope_id, - &conn->audio_socket); - *lcport = bind_port(conn->connection_ip_family, conn->self_ip_string, conn->self_scope_id, - &conn->control_socket); - *ltport = bind_port(conn->connection_ip_family, conn->self_ip_string, conn->self_scope_id, - &conn->timing_socket); + conn->local_audio_port = bind_port(conn->connection_ip_family, conn->self_ip_string, + conn->self_scope_id, &conn->audio_socket); + conn->local_control_port = bind_port(conn->connection_ip_family, conn->self_ip_string, + conn->self_scope_id, &conn->control_socket); + conn->local_timing_port = bind_port(conn->connection_ip_family, conn->self_ip_string, + conn->self_scope_id, &conn->timing_socket); - debug(2, "listening for audio, control and timing on ports %d, %d, %d.", *lsport, *lcport, - *ltport); + debug(2, "listening for audio, control and timing on ports %d, %d, %d.", conn->local_audio_port, + conn->local_control_port, conn->local_timing_port); - conn->reference_timestamp = 0; - // pthread_create(&rtp_audio_thread, NULL, &rtp_audio_receiver, NULL); - // pthread_create(&rtp_control_thread, NULL, &rtp_control_receiver, NULL); - // pthread_create(&rtp_timing_thread, NULL, &rtp_timing_receiver, NULL); + conn->reference_timestamp = 0; + // pthread_create(&rtp_audio_thread, NULL, &rtp_audio_receiver, NULL); + // pthread_create(&rtp_control_thread, NULL, &rtp_control_receiver, NULL); + // pthread_create(&rtp_timing_thread, NULL, &rtp_timing_receiver, NULL); - conn->request_sent = 0; - conn->rtp_running = 1; + conn->request_sent = 0; + conn->rtp_running = 1; #ifdef CONFIG_METADATA - send_ssnc_metadata('clip', strdup(conn->client_ip_string), strlen(conn->client_ip_string), 1); - send_ssnc_metadata('svip', strdup(conn->self_ip_string), strlen(conn->self_ip_string), 1); + send_ssnc_metadata('clip', strdup(conn->client_ip_string), strlen(conn->client_ip_string), 1); + send_ssnc_metadata('svip', strdup(conn->self_ip_string), strlen(conn->self_ip_string), 1); #endif + } } void get_reference_timestamp_stuff(int64_t *timestamp, uint64_t *timestamp_time, diff --git a/rtp.h b/rtp.h index c89f7e56..743cff52 100644 --- a/rtp.h +++ b/rtp.h @@ -12,8 +12,7 @@ void *rtp_audio_receiver(void *arg); void *rtp_control_receiver(void *arg); void *rtp_timing_receiver(void *arg); -void rtp_setup(SOCKADDR *local, SOCKADDR *remote, int controlport, int timingport, - int *local_server_port, int *local_control_port, int *local_timing_port, +void rtp_setup(SOCKADDR *local, SOCKADDR *remote, uint16_t controlport, uint16_t timingport, rtsp_conn_info *conn); void rtp_request_resend(seq_t first, uint32_t count, rtsp_conn_info *conn); void rtp_request_client_pause(rtsp_conn_info *conn); // ask the client to pause diff --git a/rtsp.c b/rtsp.c index 32fe548d..b874bfe7 100644 --- a/rtsp.c +++ b/rtsp.c @@ -668,7 +668,10 @@ static void msg_write_response(int fd, rtsp_message *resp) { static void handle_record(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { debug(2, "Connection %d: RECORD", conn->connection_number); - player_play(conn); // the thread better be 0 + if (conn->player_thread) + warn("Duplicate RECORD message -- ignored"); + else + player_play(conn); // the thread better be 0 resp->respcode = 200; // I think this is for telling the client what the absolute minimum latency @@ -700,7 +703,6 @@ static void handle_record(rtsp_conn_info *conn, rtsp_message *req, rtsp_message } } } - // usleep(500000); } static void handle_options(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message *req, @@ -724,11 +726,9 @@ static void handle_teardown(rtsp_conn_info *conn, __attribute__((unused)) rtsp_m debug(3, "TEARDOWN: synchronously terminating the player thread of RTSP conversation thread %d (2).", conn->connection_number); - // if (rtsp_playing()) { player_stop(conn); debug(3, "TEARDOWN: successful termination of playing thread of RTSP conversation thread %d.", conn->connection_number); - //} } static void handle_flush(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { @@ -763,8 +763,7 @@ static void handle_flush(rtsp_conn_info *conn, rtsp_message *req, rtsp_message * static void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { debug(2, "Connection %d: SETUP", conn->connection_number); - int cport, tport; - int lsport, lcport, ltport; + uint16_t cport, tport; char *ar = msg_get_header(req, "Active-Remote"); if (ar) { @@ -787,48 +786,52 @@ static void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message * } char *hdr = msg_get_header(req, "Transport"); - if (!hdr) + if (!hdr) { + debug(1, "SETUP doesn't contain a Transport header."); goto error; + } char *p; p = strstr(hdr, "control_port="); - if (!p) + if (!p) { + debug(1, "SETUP doesn't specify a control_port."); goto error; + } p = strchr(p, '=') + 1; cport = atoi(p); p = strstr(hdr, "timing_port="); - if (!p) + if (!p) { + debug(1, "SETUP doesn't specify a timing_port."); goto error; + } p = strchr(p, '=') + 1; tport = atoi(p); - // rtsp_take_player(); - rtp_setup(&conn->local, &conn->remote, cport, tport, &lsport, &lcport, <port, conn); - if (!lsport) - goto error; - char *q; - p = strstr(hdr, "control_port="); - if (p) { - q = strchr(p, ';'); // get past the control port entry - *p++ = 0; - if (q++) - strcat(hdr, q); // should unsplice the control port entry + if (conn->rtp_running) { + if ((conn->remote_control_port != cport) || (conn->remote_timing_port != tport)) { + warn("Duplicate SETUP message with different control (old %u, new %u) or timing (old %u, new " + "%u) ports! This is probably fatal!", + conn->remote_control_port, cport, conn->remote_timing_port, tport); + } else { + warn("Duplicate SETUP message with the same control (%u) and timing (%u) ports. This is " + "probably not fatal.", + conn->remote_control_port, conn->remote_timing_port); + } + } else { + rtp_setup(&conn->local, &conn->remote, cport, tport, conn); } - p = strstr(hdr, "timing_port="); - if (p) { - q = strchr(p, ';'); // get past the timing port entry - *p++ = 0; - if (q++) - strcat(hdr, q); // should unsplice the timing port entry + if (conn->local_audio_port == 0) { + debug(1, "SETUP seems to specify a null audio port."); + goto error; } char resphdr[256] = ""; snprintf(resphdr, sizeof(resphdr), "RTP/AVP/" - "UDP;unicast;interleaved=0-1;mode=record;control_port=%d;" - "timing_port=%d;server_" - "port=%d", - lcport, ltport, lsport); + "UDP;unicast;interleaved=0-1;mode=record;control_port=%d;" + "timing_port=%d;server_" + "port=%d", + conn->local_control_port, conn->local_timing_port, conn->local_audio_port); msg_add_header(resp, "Transport", resphdr); @@ -1435,7 +1438,7 @@ static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_messag have_the_player = 1; } else if ((playing_conn) && (playing_conn->connection_number == conn->connection_number)) { // duplicate ANNOUNCE - debug(1, "Duplicate ANNOUNCE, by the look of it!"); + warn("Duplicate ANNOUNCE, by the look of it!"); have_the_player = 1; } else { int should_wait = 0;