diff --git a/src/modules/module-rtp-session.c b/src/modules/module-rtp-session.c index 5103eac80..ba4e5a54c 100644 --- a/src/modules/module-rtp-session.c +++ b/src/modules/module-rtp-session.c @@ -527,6 +527,34 @@ static void recv_state_changed(void *data, bool started, const char *error) } } +static void recv_send_feedback(void *data, uint32_t seqnum) +{ + struct session *sess = data; + struct impl *impl = sess->impl; + struct iovec iov[1]; + struct msghdr msg; + struct rtp_apple_midi_rs hdr; + + if (!sess->ctrl_ready || !sess->receiving) + return; + + spa_zero(hdr); + hdr.cmd = htonl(APPLE_MIDI_CMD_RS); + hdr.ssrc = htonl(sess->ssrc); + hdr.seqnum = htonl(seqnum); + + iov[0].iov_base = &hdr; + iov[0].iov_len = sizeof(hdr); + + spa_zero(msg); + msg.msg_name = &sess->ctrl_addr; + msg.msg_namelen = sess->ctrl_len; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + send_packet(impl->ctrl_source->fd, &msg); +} + static const struct rtp_stream_events send_stream_events = { RTP_VERSION_STREAM_EVENTS, .destroy = send_destroy, @@ -538,6 +566,7 @@ static const struct rtp_stream_events recv_stream_events = { RTP_VERSION_STREAM_EVENTS, .destroy = recv_destroy, .state_changed = recv_state_changed, + .send_feedback = recv_send_feedback, }; static void free_session(struct session *sess) @@ -784,6 +813,31 @@ static void parse_apple_midi_cmd_ok(struct impl *impl, bool ctrl, uint8_t *buffe } } +static void parse_apple_midi_cmd_no(struct impl *impl, bool ctrl, uint8_t *buffer, + ssize_t len, struct sockaddr_storage *sa, socklen_t salen) +{ + struct rtp_apple_midi *hdr = (struct rtp_apple_midi*)buffer; + uint32_t initiator = ntohl(hdr->initiator); + struct session *sess; + + sess = find_session_by_initiator(impl, initiator, ctrl); + if (sess == NULL || !sess->we_initiated) { + pw_log_warn("received NO from nonexisting session %u", initiator); + return; + } + + if (ctrl) { + pw_log_info("got ctrl NO %08x %u", initiator, sess->data_ready); + sess->ctrl_ready = false; + } else { + pw_log_info("got data NO %08x %u, session canceled", initiator, + sess->ctrl_ready); + sess->data_ready = false; + if (!sess->ctrl_ready) + session_update_state(sess, SESSION_STATE_INIT); + } +} + static void parse_apple_midi_cmd_ck(struct impl *impl, bool ctrl, uint8_t *buffer, ssize_t len, struct sockaddr_storage *sa, socklen_t salen) { @@ -878,6 +932,24 @@ static void parse_apple_midi_cmd_by(struct impl *impl, bool ctrl, uint8_t *buffe } } +static void parse_apple_midi_cmd_rs(struct impl *impl, bool ctrl, uint8_t *buffer, + ssize_t len, struct sockaddr_storage *sa, socklen_t salen) +{ + struct rtp_apple_midi_rs *hdr = (struct rtp_apple_midi_rs*)buffer; + struct session *sess; + uint32_t ssrc, seqnum; + + ssrc = ntohl(hdr->ssrc); + sess = find_session_by_ssrc(impl, ssrc); + if (sess == NULL) { + pw_log_warn("unknown SSRC %u", ssrc); + return; + } + + seqnum = ntohl(hdr->seqnum); + pw_log_debug("got RS seqnum %u", seqnum); +} + static void parse_apple_midi_cmd(struct impl *impl, bool ctrl, uint8_t *buffer, ssize_t len, struct sockaddr_storage *sa, socklen_t salen) { @@ -889,12 +961,18 @@ static void parse_apple_midi_cmd(struct impl *impl, bool ctrl, uint8_t *buffer, case APPLE_MIDI_CMD_OK: parse_apple_midi_cmd_ok(impl, ctrl, buffer, len, sa, salen); break; + case APPLE_MIDI_CMD_NO: + parse_apple_midi_cmd_no(impl, ctrl, buffer, len, sa, salen); + break; case APPLE_MIDI_CMD_CK: parse_apple_midi_cmd_ck(impl, ctrl, buffer, len, sa, salen); break; case APPLE_MIDI_CMD_BY: parse_apple_midi_cmd_by(impl, ctrl, buffer, len, sa, salen); break; + case APPLE_MIDI_CMD_RS: + parse_apple_midi_cmd_rs(impl, ctrl, buffer, len, sa, salen); + break; default: break; } diff --git a/src/modules/module-rtp/apple-midi.h b/src/modules/module-rtp/apple-midi.h index a1b20b396..2ef29afe2 100644 --- a/src/modules/module-rtp/apple-midi.h +++ b/src/modules/module-rtp/apple-midi.h @@ -32,11 +32,18 @@ struct rtp_apple_midi_ck { uint32_t ts3_l; } __attribute__ ((packed)); +struct rtp_apple_midi_rs { + uint32_t cmd; + uint32_t ssrc; + uint32_t seqnum; +} __attribute__ ((packed)); + #define APPLE_MIDI_CMD_IN (0xffff0000 | 'I'<<8 | 'N') #define APPLE_MIDI_CMD_NO (0xffff0000 | 'N'<<8 | 'O') #define APPLE_MIDI_CMD_OK (0xffff0000 | 'O'<<8 | 'K') #define APPLE_MIDI_CMD_CK (0xffff0000 | 'C'<<8 | 'K') #define APPLE_MIDI_CMD_BY (0xffff0000 | 'B'<<8 | 'Y') +#define APPLE_MIDI_CMD_RS (0xffff0000 | 'R'<<8 | 'S') #ifdef __cplusplus } diff --git a/src/modules/module-rtp/audio.c b/src/modules/module-rtp/audio.c index 3612ad470..452aa28ac 100644 --- a/src/modules/module-rtp/audio.c +++ b/src/modules/module-rtp/audio.c @@ -142,7 +142,7 @@ static int receive_rtp_audio(struct impl *impl, uint8_t *buffer, ssize_t len) if (!impl->have_sync) { pw_log_info("sync to timestamp:%u seq:%u ts_offset:%u SSRC:%u target:%u direct:%u", - write, impl->seq-1, impl->ts_offset, impl->ssrc, + timestamp, seq, impl->ts_offset, impl->ssrc, impl->target_buffer, impl->direct_timestamp); /* we read from timestamp, keeping target_buffer of data diff --git a/src/modules/module-rtp/midi.c b/src/modules/module-rtp/midi.c index bb9ae4a88..1eda8fd2a 100644 --- a/src/modules/module-rtp/midi.c +++ b/src/modules/module-rtp/midi.c @@ -126,6 +126,13 @@ static int get_midi_size(uint8_t *p, uint32_t avail) } return size; } +static int parse_journal(struct impl *impl, uint8_t *packet, uint16_t seq, uint32_t len) +{ + struct rtp_midi_journal *j = (struct rtp_midi_journal*)packet; + uint16_t seqnum = ntohs(j->checkpoint_seqnum); + rtp_stream_emit_send_feedback(impl, seqnum); + return 0; +} static double get_time(struct impl *impl) { @@ -143,8 +150,8 @@ static double get_time(struct impl *impl) return t; } -static int receive_midi(struct impl *impl, uint8_t *packet, - uint32_t timestamp, uint32_t payload_offset, uint32_t plen) +static int receive_midi(struct impl *impl, uint8_t *packet, uint32_t timestamp, + uint16_t seq, uint32_t payload_offset, uint32_t plen) { uint32_t write; struct rtp_midi_header *hdr; @@ -160,7 +167,7 @@ static int receive_midi(struct impl *impl, uint8_t *packet, * midi events and render them in the corresponding cycle */ if (!impl->have_sync) { pw_log_info("sync to timestamp:%u seq:%u ts_offset:%u SSRC:%u direct:%d", - timestamp, impl->seq-1, impl->ts_offset, impl->ssrc, + timestamp, seq, impl->ts_offset, impl->ssrc, impl->direct_timestamp); impl->have_sync = true; } @@ -186,7 +193,7 @@ static int receive_midi(struct impl *impl, uint8_t *packet, spa_dll_set_bw(&impl->dll, SPA_DLL_BW_MIN, 256, impl->rate); pw_log_info("sync to timestamp:%u seq:%u ts_offset:%u SSRC:%u direct:%d", - timestamp, impl->seq-1, impl->ts_offset, impl->ssrc, + timestamp, seq, impl->ts_offset, impl->ssrc, impl->direct_timestamp); impl->have_sync = true; impl->ring.readindex = impl->ring.writeindex; @@ -221,6 +228,8 @@ static int receive_midi(struct impl *impl, uint8_t *packet, pw_log_warn("invalid packet %d > %d", end, plen); return -EINVAL; } + if (hdr->j) + parse_journal(impl, &packet[end], seq, plen - end); ptr = SPA_PTROFF(impl->buffer, write & BUFFER_MASK2, void); @@ -258,6 +267,8 @@ static int receive_midi(struct impl *impl, uint8_t *packet, write += b.state.offset; spa_ringbuffer_write_update(&impl->ring, write); + + return 0; } @@ -297,7 +308,7 @@ static int receive_rtp_midi(struct impl *impl, uint8_t *buffer, ssize_t len) impl->receiving = true; - return receive_midi(impl, buffer, timestamp, hlen, len); + return receive_midi(impl, buffer, timestamp, seq, hlen, len); short_packet: pw_log_warn("short packet received"); diff --git a/src/modules/module-rtp/rtp.h b/src/modules/module-rtp/rtp.h index 043a41f8c..1fd3bedef 100644 --- a/src/modules/module-rtp/rtp.h +++ b/src/modules/module-rtp/rtp.h @@ -58,17 +58,34 @@ struct rtp_midi_header { unsigned z:1; unsigned j:1; unsigned b:1; - uint8_t len_b; #elif __BYTE_ORDER == __BIG_ENDIAN unsigned b:1; unsigned j:1; unsigned z:1; unsigned p:1; unsigned len:4; - uint8_t len_b; #endif + uint8_t len_b; } __attribute__ ((packed)); +struct rtp_midi_journal { +#if __BYTE_ORDER == __LITTLE_ENDIAN + unsigned totchan:4; + unsigned H:1; + unsigned A:1; + unsigned Y:1; + unsigned S:1; +#elif __BYTE_ORDER == __BIG_ENDIAN + unsigned S:1; + unsigned Y:1; + unsigned A:1; + unsigned H:1; + unsigned totchan:4; +#endif + uint16_t checkpoint_seqnum; +} __attribute__ ((packed)); + + #ifdef __cplusplus } #endif diff --git a/src/modules/module-rtp/stream.c b/src/modules/module-rtp/stream.c index 1a251e465..975f83b47 100644 --- a/src/modules/module-rtp/stream.c +++ b/src/modules/module-rtp/stream.c @@ -31,6 +31,7 @@ #define rtp_stream_emit_destroy(s) rtp_stream_emit(s, destroy, 0) #define rtp_stream_emit_state_changed(s,n,e) rtp_stream_emit(s, state_changed,0,n,e) #define rtp_stream_emit_send_packet(s,i,l) rtp_stream_emit(s, send_packet,0,i,l) +#define rtp_stream_emit_send_feedback(s,seq) rtp_stream_emit(s, send_feedback,0,seq) struct impl { struct spa_audio_info info; diff --git a/src/modules/module-rtp/stream.h b/src/modules/module-rtp/stream.h index 1af20b1d4..7686c565e 100644 --- a/src/modules/module-rtp/stream.h +++ b/src/modules/module-rtp/stream.h @@ -32,6 +32,8 @@ struct rtp_stream_events { void (*state_changed) (void *data, bool started, const char *error); void (*send_packet) (void *data, struct iovec *iov, size_t iovlen); + + void (*send_feedback) (void *data, uint32_t senum); }; struct rtp_stream *rtp_stream_new(struct pw_core *core,