Skip to content

Commit dce6789

Browse files
committed
Restoring NAL splitting for RTSP, preventing torn frames on slow HTTP streams
- Restored the NAL split loop in rtp_send_h26x() to correctly handle Annex-B prefixed packs, ensuring proper identification of NAL headers. - Rewrote the server's send helpers to handle partial writes and implement a bounded 100ms timeout budget for slow clients. - Added dead-peer detection and fixed the final chunked terminator length. Closes #37.
1 parent ad3f0b6 commit dce6789

2 files changed

Lines changed: 58 additions & 18 deletions

File tree

src/rtsp/rtp.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -439,9 +439,17 @@ int rtp_send_h26x(rtsp_handle h, hal_vidstream *stream, char isH265)
439439

440440
if (trans.list_head.list) {
441441
for (int i = 0; i < stream->count; i++) {
442-
ASSERT(__transfer_nal_h26x(&(trans.list_head),
443-
stream->pack[i].data + stream->pack[i].offset,
444-
stream->pack[i].length - stream->pack[i].offset, h->isH265) == SUCCESS, goto error);
442+
unsigned char *data = stream->pack[i].data + stream->pack[i].offset;
443+
size_t length = stream->pack[i].length - stream->pack[i].offset;
444+
if (length >= 4 && data[0] == 0 && data[1] == 0 && data[2] == 0 && data[3] == 1) {
445+
unsigned char *nalptr = data;
446+
size_t single_len = 0;
447+
while (__split_nal(data, &nalptr, &single_len, length) == SUCCESS) {
448+
ASSERT(__transfer_nal_h26x(&(trans.list_head), nalptr, single_len, h->isH265) == SUCCESS, goto error);
449+
}
450+
} else {
451+
ASSERT(__transfer_nal_h26x(&(trans.list_head), data, length, h->isH265) == SUCCESS, goto error);
452+
}
445453
}
446454
ASSERT(list_map_inline(&(trans.list_head), (__rtcp_poll), &track_id) == SUCCESS, goto error);
447455
}

src/server.c

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -87,23 +87,55 @@ void free_client(int i) {
8787

8888
int send_to_fd(int fd, char *buf, ssize_t size) {
8989
if (fd < 0) return -1;
90-
ssize_t sent = send(fd, buf, size, MSG_DONTWAIT | MSG_NOSIGNAL);
91-
if (sent < 0) {
92-
if (errno == EAGAIN || errno == EWOULDBLOCK) return 0;
93-
return -1;
90+
91+
ssize_t total = 0;
92+
for (int attempts = 0; attempts < 4; attempts++) {
93+
ssize_t n = send(fd, buf + total, size - total, MSG_DONTWAIT | MSG_NOSIGNAL);
94+
if (n > 0) {
95+
total += n;
96+
attempts = -1;
97+
} else if (n == 0) return -1;
98+
else if (errno != EAGAIN && errno != EWOULDBLOCK) return -1;
99+
100+
if (total == size) return EXIT_SUCCESS;
101+
102+
struct pollfd pfd = { .fd = fd, .events = POLLOUT };
103+
if (poll(&pfd, 1, 25) < 0) return -1;
94104
}
95-
return EXIT_SUCCESS;
105+
106+
return -1;
96107
}
97108

98109
int sendv_to_client(int i, struct iovec *iov, int iovcnt) {
99-
ssize_t n = writev(client_fds[i].sockFd, iov, iovcnt);
100-
if (n < 0) {
101-
if (errno == EAGAIN || errno == EWOULDBLOCK) return 0;
102-
if (errno == EINTR) return 0;
103-
free_client(i);
104-
return -1;
110+
int fd = client_fds[i].sockFd;
111+
if (fd < 0) return -1;
112+
113+
for (int attempts = 0; attempts < 4; attempts++) {
114+
ssize_t n = writev(fd, iov, iovcnt);
115+
if (n > 0) {
116+
while (n > 0 && iovcnt > 0) {
117+
if (n >= iov[0].iov_len) {
118+
n -= iov[0].iov_len;
119+
iov++;
120+
iovcnt--;
121+
} else {
122+
iov[0].iov_base = (char *)iov[0].iov_base + n;
123+
iov[0].iov_len -= n;
124+
n = 0;
125+
}
126+
}
127+
if (iovcnt == 0) return 0;
128+
attempts = -1;
129+
} else if (n == 0) goto error;
130+
else if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) goto error;
131+
132+
struct pollfd pfd = { .fd = fd, .events = POLLOUT };
133+
if (poll(&pfd, 1, 25) < 0) goto error;
105134
}
106-
return 0;
135+
136+
error:
137+
free_client(i);
138+
return -1;
107139
}
108140

109141
int send_to_client(int i, char *buf, ssize_t size) {
@@ -424,11 +456,11 @@ int send_file(const int client_fd, const char *path) {
424456
len_size = sprintf(len_buf, "%zX\r\n", size);
425457
buf[size++] = '\r';
426458
buf[size++] = '\n';
427-
send_to_fd(client_fd, len_buf, len_size); // send <SIZE>\r\n
428-
send_to_fd(client_fd, buf, size); // send <DATA>\r\n
459+
if (send_to_fd(client_fd, len_buf, len_size) < 0) break;
460+
if (send_to_fd(client_fd, buf, size) < 0) break;
429461
}
430462
char end[] = "0\r\n\r\n";
431-
send_to_fd(client_fd, end, sizeof(end));
463+
send_to_fd(client_fd, end, sizeof(end) - 1);
432464
fclose(file);
433465
close_socket_fd(client_fd);
434466
return EXIT_FAILURE;

0 commit comments

Comments
 (0)