Allow sps to deal with duplicate RTSP messages like RECORD and SETUP. Add some diagnostics to the player.

This commit is contained in:
Mike Brady
2018-04-10 17:35:39 +01:00
parent a9f5ce731b
commit 665a53a8b5
5 changed files with 146 additions and 127 deletions
+9 -4
View File
@@ -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;
+6
View File
@@ -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;
+96 -90
View File
@@ -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,
+1 -2
View File
@@ -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
+34 -31
View File
@@ -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, &ltport, 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;