player.c compiles and everything builds and runs with the airplay 2 flag off. With airplay_2 flag on, rtp.c won't build, but we know that.
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
* Copyright (c) James Laird 2011, 2013
|
||||
* All rights reserved.
|
||||
*
|
||||
* Modifications for audio synchronisation
|
||||
* Modifications for audio synchronisation, AirPlay 2
|
||||
* and related work, copyright (c) Mike Brady 2014 -- 2021
|
||||
* All rights reserved.
|
||||
*
|
||||
@@ -110,12 +110,7 @@ int64_t first_frame_early_bias = 8;
|
||||
// static abuf_t audio_buffer[BUFFER_FRAMES];
|
||||
#define BUFIDX(seqno) ((seq_t)(seqno) % BUFFER_FRAMES)
|
||||
|
||||
uint32_t modulo_32_offset(uint32_t from, uint32_t to) {
|
||||
if (from <= to)
|
||||
return to - from;
|
||||
else
|
||||
return UINT32_MAX - from + to + 1;
|
||||
}
|
||||
int32_t modulo_32_offset(uint32_t from, uint32_t to) { return to - from; }
|
||||
|
||||
void do_flush(uint32_t timestamp, rtsp_conn_info *conn);
|
||||
|
||||
@@ -141,79 +136,34 @@ static void ab_resync(rtsp_conn_info *conn) {
|
||||
// returns true if x is actually within the buffer
|
||||
|
||||
int position_in_modulo_uint16_t_buffer(uint16_t x, uint16_t start, uint16_t end, uint16_t *pos) {
|
||||
int response = 0; // not in the buffer
|
||||
if (start <= end) {
|
||||
if (x < start) { // this means that it's up around the wrap
|
||||
if (pos)
|
||||
*pos = UINT16_MAX - start + 1 + x;
|
||||
} else {
|
||||
if (pos)
|
||||
*pos = x - start;
|
||||
if (x < end)
|
||||
response = 1;
|
||||
}
|
||||
} else if ((x >= start)) { // && (x <= UINT16_MAX)) { // always true
|
||||
int response = 0;
|
||||
|
||||
int16_t x_disp = x - start;
|
||||
int16_t b_size =
|
||||
end - start; // this will be positive as long as (a) start and end are within 2^15 of each
|
||||
// other and (b) end is at or after start on the modulo wheel
|
||||
if ((x_disp >= 0) && (x_disp < b_size))
|
||||
response = 1;
|
||||
if (pos)
|
||||
*pos = x - start;
|
||||
} else {
|
||||
if (pos)
|
||||
*pos = UINT16_MAX - start + 1 + x;
|
||||
if (x < end) {
|
||||
response = 1;
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
// wrapped number between two seq_t.
|
||||
// as long as the two seq_t numbers are supposed to be within 2^15 of one another,
|
||||
// that is, you go the shorter way around the 2^16 modulo wheel,
|
||||
// this will return positive if b is equal to or after a and negative otherwise
|
||||
int32_t seq_diff(seq_t a, seq_t b, seq_t base) {
|
||||
int16_t d = b - a;
|
||||
return d;
|
||||
}
|
||||
|
||||
static inline seq_t SUCCESSOR(seq_t x) { return x + 1; }
|
||||
|
||||
// a minus b
|
||||
int16_t seq_diff(seq_t a, seq_t b) {
|
||||
int16_t response;
|
||||
seq_t diff = a - b;
|
||||
seq_t invdiff = b - a;
|
||||
if (diff < invdiff)
|
||||
response = diff;
|
||||
else
|
||||
response = -invdiff;
|
||||
return response;
|
||||
// the sequence numbers will wrap pretty often.
|
||||
// this returns true if the second arg is strictly after the first
|
||||
static inline int seq_order(seq_t a, seq_t b, seq_t base) {
|
||||
int16_t d = b - a;
|
||||
return d > 0;
|
||||
}
|
||||
|
||||
static inline seq_t seq_sum(seq_t a, seq_t b) { return a + b; }
|
||||
|
||||
// This orders u and v by picking the smaller of the two modulo differences
|
||||
// in unsigned modulo arithmetic and setting the sign of the result accordingly.
|
||||
|
||||
// if u - v gives the smaller modulo difference, then that modulo difference is returned
|
||||
// otherwise the negative of the v - u modulo difference is returned.
|
||||
|
||||
// (Think of a modulo ring starting at 0 and circling around clockwise finally to
|
||||
// modulo-2, modulo-1 to 0 again. To determine the ordering of the two numbers,
|
||||
// Find the shorter path between them around the ring.
|
||||
// If you go from v to u in the positive (0, 1, 2 ... or clockwise) direction,
|
||||
// then u is greater than (or "after") v.)
|
||||
|
||||
// The result will always fit in an int64_t as it must be less or equal to half way
|
||||
// around the ring.
|
||||
// Due to the asymmetry of 2's complement representation, however,
|
||||
// if the difference is equal to modulo / 2, it is ambiguous as to whether
|
||||
// u is "after" or "before" (i.e. greater or less than) v.
|
||||
// and will be returned as -modulo / 2, that is, "before".
|
||||
// If that ever happens and there is a real ambiguity in the application,
|
||||
// the modulo chosen is too small.
|
||||
|
||||
int64_t int64_mod_difference(const uint64_t u, const uint64_t v, const uint64_t modulo) {
|
||||
int64_t response;
|
||||
uint64_t diff = (u - v) % modulo;
|
||||
uint64_t invdiff = (v - u) % modulo;
|
||||
if (diff < invdiff)
|
||||
response = diff;
|
||||
else
|
||||
response = -invdiff;
|
||||
return response;
|
||||
}
|
||||
|
||||
void reset_input_flow_metrics(rtsp_conn_info *conn) {
|
||||
conn->play_number_after_flush = 0;
|
||||
conn->packet_count_since_flush = 0;
|
||||
@@ -444,8 +394,40 @@ static void free_audio_buffers(rtsp_conn_info *conn) {
|
||||
|
||||
int first_possibly_missing_frame = -1;
|
||||
|
||||
void player_put_packet(seq_t seqno, uint32_t actual_timestamp, uint8_t *data, int len,
|
||||
rtsp_conn_info *conn) {
|
||||
void reset_buffer(rtsp_conn_info *conn) {
|
||||
debug_mutex_lock(&conn->ab_mutex, 30000, 0);
|
||||
ab_resync(conn);
|
||||
debug_mutex_unlock(&conn->ab_mutex, 0);
|
||||
}
|
||||
|
||||
void get_audio_buffer_size_and_occupancy(unsigned int *size, unsigned int *occupancy,
|
||||
rtsp_conn_info *conn) {
|
||||
debug_mutex_lock(&conn->ab_mutex, 30000, 0);
|
||||
*size = BUFFER_FRAMES;
|
||||
if (conn->ab_synced) {
|
||||
int16_t occ =
|
||||
conn->ab_write - conn->ab_read; // will be zero or positive if read and write are within
|
||||
// 2^15 of each other and write is at or after read
|
||||
*occupancy = occ;
|
||||
} else {
|
||||
*occupancy = 0;
|
||||
}
|
||||
debug_mutex_unlock(&conn->ab_mutex, 0);
|
||||
}
|
||||
|
||||
void player_put_packet(int original_format, seq_t seqno, uint32_t actual_timestamp, uint8_t *data,
|
||||
int len, rtsp_conn_info *conn) {
|
||||
|
||||
// if it's original format, it has a valid seqno and must be decoded
|
||||
// otherwise, it can take the next seqno and doesn't need decoding.
|
||||
|
||||
// ignore a request to flush that has been made before the first packet...
|
||||
if (conn->packet_count == 0) {
|
||||
debug_mutex_lock(&conn->flush_mutex, 1000, 1);
|
||||
conn->flush_requested = 0;
|
||||
conn->flush_rtp_timestamp = 0;
|
||||
debug_mutex_unlock(&conn->flush_mutex, 3);
|
||||
}
|
||||
|
||||
debug_mutex_lock(&conn->ab_mutex, 30000, 0);
|
||||
uint64_t time_now = get_absolute_time_in_ns();
|
||||
@@ -453,77 +435,93 @@ void player_put_packet(seq_t seqno, uint32_t actual_timestamp, uint8_t *data, in
|
||||
conn->packet_count_since_flush++;
|
||||
conn->time_of_last_audio_packet = time_now;
|
||||
if (conn->connection_state_to_output) { // if we are supposed to be processing these packets
|
||||
abuf_t *abuf = 0;
|
||||
if (!conn->ab_synced) {
|
||||
// if this is the first packet...
|
||||
debug(3, "syncing to seqno %u.", seqno);
|
||||
conn->ab_write = seqno;
|
||||
conn->ab_read = seqno;
|
||||
conn->ab_synced = 1;
|
||||
}
|
||||
int16_t write_point_gap = seq_diff(seqno, conn->ab_write); // this is the difference between
|
||||
// the incoming packet number and the packet number that was expected.
|
||||
if (write_point_gap ==
|
||||
0) { // if this is the expected packet (which could be the first packet...)
|
||||
if (conn->input_frame_rate_starting_point_is_valid == 0) {
|
||||
if ((conn->packet_count_since_flush >= 500) && (conn->packet_count_since_flush <= 510)) {
|
||||
conn->frames_inward_measurement_start_time = time_now;
|
||||
conn->frames_inward_frames_received_at_measurement_start_time = actual_timestamp;
|
||||
conn->input_frame_rate_starting_point_is_valid = 1; // valid now
|
||||
abuf_t *abuf = 0;
|
||||
if (!conn->ab_synced) {
|
||||
// if this is the first packet...
|
||||
debug(3, "syncing to seqno %u.", seqno);
|
||||
conn->ab_write = seqno;
|
||||
conn->ab_read = seqno;
|
||||
conn->ab_synced = 1;
|
||||
} else if (original_format == 0) {
|
||||
// if the packet is coming in original format, the sequence number is important
|
||||
// otherwise, ignore is by setting it equal to the expected sequence number in ab_write
|
||||
seqno = conn->ab_write;
|
||||
}
|
||||
if (conn->ab_write ==
|
||||
seqno) { // if this is the expected packet (which could be the first packet...)
|
||||
if (conn->input_frame_rate_starting_point_is_valid == 0) {
|
||||
if ((conn->packet_count_since_flush >= 500) && (conn->packet_count_since_flush <= 510)) {
|
||||
conn->frames_inward_measurement_start_time = time_now;
|
||||
conn->frames_inward_frames_received_at_measurement_start_time = actual_timestamp;
|
||||
conn->input_frame_rate_starting_point_is_valid = 1; // valid now
|
||||
}
|
||||
}
|
||||
conn->frames_inward_measurement_time = time_now;
|
||||
conn->frames_inward_frames_received_at_measurement_time = actual_timestamp;
|
||||
abuf = conn->audio_buffer + BUFIDX(seqno);
|
||||
conn->ab_write = seqno + 1; // move the write pointer to the next free space
|
||||
} else if (seq_order(conn->ab_write, seqno, conn->ab_read)) { // newer than expected
|
||||
int32_t gap = seq_diff(conn->ab_write, seqno, conn->ab_read);
|
||||
if (gap <= 0)
|
||||
debug(1, "Unexpected gap size: %d.", gap);
|
||||
int i;
|
||||
for (i = 0; i < gap; i++) {
|
||||
abuf = conn->audio_buffer + BUFIDX(seq_sum(conn->ab_write, i));
|
||||
abuf->ready = 0; // to be sure, to be sure
|
||||
abuf->resend_request_number = 0;
|
||||
abuf->initialisation_time =
|
||||
time_now; // this represents when the packet was noticed to be missing
|
||||
abuf->status = 1 << 0; // signifying missing
|
||||
abuf->resend_time = 0;
|
||||
abuf->given_timestamp = 0;
|
||||
abuf->sequence_number = 0;
|
||||
}
|
||||
// debug(1,"N %d s %u.",seq_diff(ab_write,PREDECESSOR(seqno))+1,ab_write);
|
||||
abuf = conn->audio_buffer + BUFIDX(seqno);
|
||||
// rtp_request_resend(ab_write, gap);
|
||||
// resend_requests++;
|
||||
conn->ab_write = seqno + 1;
|
||||
} else if (seq_order(conn->ab_read, seqno,
|
||||
conn->ab_read)) { // older than expected but not too late
|
||||
conn->late_packets++;
|
||||
abuf = conn->audio_buffer + BUFIDX(seqno);
|
||||
} else { // too late.
|
||||
conn->too_late_packets++;
|
||||
}
|
||||
|
||||
if (abuf) {
|
||||
int datalen = conn->max_frames_per_packet;
|
||||
abuf->initialisation_time = time_now;
|
||||
abuf->resend_time = 0;
|
||||
if ((original_format != 0) &&
|
||||
(audio_packet_decode(abuf->data, &datalen, data, len, conn) == 0)) {
|
||||
abuf->ready = 1;
|
||||
abuf->status = 0; // signifying that it was received
|
||||
abuf->length = datalen;
|
||||
abuf->given_timestamp = actual_timestamp;
|
||||
abuf->sequence_number = seqno;
|
||||
} else if (original_format == 0) {
|
||||
memcpy(abuf->data, data, len * conn->input_bytes_per_frame);
|
||||
abuf->ready = 1;
|
||||
abuf->status = 0; // signifying that it was received
|
||||
abuf->length = len;
|
||||
abuf->given_timestamp = actual_timestamp;
|
||||
abuf->sequence_number = seqno;
|
||||
} else {
|
||||
debug(1, "Bad audio packet detected and discarded.");
|
||||
abuf->ready = 0;
|
||||
abuf->status = 1 << 1; // bad packet, discarded
|
||||
abuf->resend_request_number = 0;
|
||||
abuf->given_timestamp = 0;
|
||||
abuf->sequence_number = 0;
|
||||
}
|
||||
}
|
||||
conn->frames_inward_measurement_time = time_now;
|
||||
conn->frames_inward_frames_received_at_measurement_time = actual_timestamp;
|
||||
abuf = conn->audio_buffer + BUFIDX(seqno);
|
||||
conn->ab_write = SUCCESSOR(seqno); // move the write pointer to the next free space
|
||||
} else if (write_point_gap > 0) { // newer than expected
|
||||
// initialise the frames in between
|
||||
int i;
|
||||
for (i = 0; i < write_point_gap; i++) {
|
||||
abuf = conn->audio_buffer + BUFIDX(seq_sum(conn->ab_write, i));
|
||||
abuf->ready = 0; // to be sure, to be sure
|
||||
abuf->resend_request_number = 0;
|
||||
abuf->initialisation_time =
|
||||
time_now; // this represents when the packet was noticed to be missing
|
||||
abuf->status = 1 << 0; // signifying missing
|
||||
abuf->resend_time = 0;
|
||||
abuf->given_timestamp = 0;
|
||||
abuf->sequence_number = 0;
|
||||
}
|
||||
abuf = conn->audio_buffer + BUFIDX(seqno);
|
||||
conn->ab_write = SUCCESSOR(seqno);
|
||||
} else if (seq_diff(seqno, conn->ab_read) > 0) { // older than expected but still not too late
|
||||
conn->late_packets++;
|
||||
abuf = conn->audio_buffer + BUFIDX(seqno);
|
||||
} else { // too late.
|
||||
conn->too_late_packets++;
|
||||
}
|
||||
|
||||
if (abuf) {
|
||||
int datalen = conn->max_frames_per_packet;
|
||||
abuf->initialisation_time = time_now;
|
||||
abuf->resend_time = 0;
|
||||
if (audio_packet_decode(abuf->data, &datalen, data, len, conn) == 0) {
|
||||
abuf->ready = 1;
|
||||
abuf->status = 0; // signifying that it was received
|
||||
abuf->length = datalen;
|
||||
abuf->given_timestamp = actual_timestamp;
|
||||
abuf->sequence_number = seqno;
|
||||
} else {
|
||||
debug(1, "Bad audio packet detected and discarded.");
|
||||
abuf->ready = 0;
|
||||
abuf->status = 1 << 1; // bad packet, discarded
|
||||
abuf->resend_request_number = 0;
|
||||
abuf->given_timestamp = 0;
|
||||
abuf->sequence_number = 0;
|
||||
}
|
||||
}
|
||||
int rc = pthread_cond_signal(&conn->flowcontrol);
|
||||
if (rc)
|
||||
debug(1, "Error signalling flowcontrol.");
|
||||
|
||||
int rc = pthread_cond_signal(&conn->flowcontrol);
|
||||
if (rc)
|
||||
debug(1, "Error signalling flowcontrol.");
|
||||
|
||||
// resend checks
|
||||
// resend checks
|
||||
{
|
||||
uint64_t minimum_wait_time =
|
||||
(uint64_t)(config.resend_control_first_check_time * (uint64_t)1000000000);
|
||||
@@ -1252,6 +1250,29 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
|
||||
if (local_time_now >= time_to_play) {
|
||||
do_wait = 0;
|
||||
}
|
||||
// here, do a sanity check. if the time_to_play is not within a few seconds of the
|
||||
// time now, the frame is probably not meant to be there, so let it go.
|
||||
if (do_wait != 0) {
|
||||
// this is a hack.
|
||||
// we subtract two 2^n unsigned numbers and get a signed 2^n result.
|
||||
// If we think of the calculation as occurring in modulo 2^n arithmetic
|
||||
// then the signed result's magnitude represents the shorter distance around
|
||||
// the modulo wheel of values from one number to the other.
|
||||
// The sign indicates the direction: positive means clockwise (upwards) from the
|
||||
// second number to the first (i.e. the first number comes "after" the second).
|
||||
|
||||
int64_t time_difference = local_time_now - time_to_play;
|
||||
if ((time_difference > 10000000000) || (time_difference < -10000000000)) {
|
||||
debug(2,
|
||||
"crazy time interval of %f seconds between time now: 0x%" PRIx64
|
||||
" and time of packet: %" PRIx64 ".",
|
||||
0.000000001 * time_difference, local_time_now, time_to_play);
|
||||
debug(2, "packet rtptime: %u, reference_timestamp: %u", curframe->given_timestamp,
|
||||
conn->anchor_rtptime);
|
||||
|
||||
do_wait = 0; // let it go
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (do_wait == 0)
|
||||
@@ -1307,7 +1328,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
|
||||
}
|
||||
curframe->ready = 0;
|
||||
}
|
||||
conn->ab_read = SUCCESSOR(conn->ab_read);
|
||||
conn->ab_read++;
|
||||
pthread_cleanup_pop(1);
|
||||
return curframe;
|
||||
}
|
||||
@@ -1544,6 +1565,40 @@ void player_thread_cleanup_handler(void *arg) {
|
||||
mdns_dacp_monitor_set_id(NULL); // say we're not interested in following that DACP id any more
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_AIRPORT_2
|
||||
if (conn->rtp_ap2_control_thread) {
|
||||
debug(3, "Cancelling rtp_ap2_control_thread");
|
||||
pthread_cancel(conn->rtp_ap2_control_thread);
|
||||
pthread_join(conn->rtp_ap2_control_thread, NULL);
|
||||
}
|
||||
if (conn->ap2_control_socket) {
|
||||
close(conn->ap2_control_socket);
|
||||
conn->ap2_control_socket = 0;
|
||||
}
|
||||
|
||||
// realtime stuff
|
||||
if (conn->rtp_realtime_audio_thread) {
|
||||
debug(3, "Cancelling rtp_realtime_audio_thread");
|
||||
pthread_cancel(conn->rtp_realtime_audio_thread);
|
||||
pthread_join(conn->rtp_realtime_audio_thread, NULL);
|
||||
}
|
||||
if (conn->realtime_audio_socket) {
|
||||
close(conn->realtime_audio_socket);
|
||||
conn->realtime_audio_socket = 0;
|
||||
}
|
||||
|
||||
// buffered stuff
|
||||
if (conn->rtp_buffered_audio_thread) {
|
||||
debug(3, "Cancelling rtp_buffered_audio_thread");
|
||||
pthread_cancel(conn->rtp_buffered_audio_thread);
|
||||
pthread_join(conn->rtp_buffered_audio_thread, NULL);
|
||||
}
|
||||
if (conn->buffered_audio_socket) {
|
||||
close(conn->buffered_audio_socket);
|
||||
conn->buffered_audio_socket = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
debug(3, "Cancelling timing, control and audio threads...");
|
||||
debug(3, "Cancel timing thread.");
|
||||
pthread_cancel(conn->rtp_timing_thread);
|
||||
@@ -1604,11 +1659,7 @@ void *player_thread_func(void *arg) {
|
||||
// be used as a null operand, so we'll use it like that too
|
||||
conn->fix_volume = 0x10000;
|
||||
|
||||
if (conn->latency == 0) {
|
||||
debug(3, "No latency has (yet) been specified. Setting 88,200 (2 seconds) frames "
|
||||
"as a default.");
|
||||
conn->latency = 88200;
|
||||
}
|
||||
reset_anchor_info(conn);
|
||||
|
||||
if (conn->stream.type == ast_apple_lossless)
|
||||
init_alac_decoder((int32_t *)&conn->stream.fmtp,
|
||||
@@ -1870,10 +1921,12 @@ void *player_thread_func(void *arg) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef CONFIG_AIRPLAY_2
|
||||
// create and start the timing, control and audio receiver threads
|
||||
pthread_create(&conn->rtp_audio_thread, NULL, &rtp_audio_receiver, (void *)conn);
|
||||
pthread_create(&conn->rtp_control_thread, NULL, &rtp_control_receiver, (void *)conn);
|
||||
pthread_create(&conn->rtp_timing_thread, NULL, &rtp_timing_receiver, (void *)conn);
|
||||
#endif
|
||||
|
||||
pthread_cleanup_push(player_thread_cleanup_handler, arg); // undo what's been done so far
|
||||
|
||||
@@ -1910,10 +1963,10 @@ void *player_thread_func(void *arg) {
|
||||
debug(2,
|
||||
"Player has supplied a silent frame, (possibly frame %u) for play number %d, "
|
||||
"status 0x%X after %u resend requests.",
|
||||
SUCCESSOR(conn->last_seqno_read), play_number, inframe->status,
|
||||
conn->last_seqno_read + 1, play_number, inframe->status,
|
||||
inframe->resend_request_number);
|
||||
conn->last_seqno_read =
|
||||
SUCCESSOR(conn->last_seqno_read); // manage the packet out of sequence minder
|
||||
((conn->last_seqno_read + 1) & 0xffff); // manage the packet out of sequence minder
|
||||
|
||||
void *silence = malloc(conn->output_bytes_per_frame * conn->max_frames_per_packet *
|
||||
conn->output_sample_ratio);
|
||||
@@ -2050,7 +2103,7 @@ void *player_thread_func(void *arg) {
|
||||
inframe->sequence_number; // int32_t from seq_t, i.e. uint16_t, so okay.
|
||||
else {
|
||||
conn->last_seqno_read =
|
||||
SUCCESSOR(conn->last_seqno_read); // int32_t from seq_t, i.e. uint16_t, so okay.
|
||||
(conn->last_seqno_read + 1) & 0xffff; // int32_t from seq_t, i.e. uint16_t, so okay.
|
||||
if (inframe->sequence_number !=
|
||||
conn->last_seqno_read) { // seq_t, ei.e. uint16_t and int32_t, so okay
|
||||
debug(2,
|
||||
@@ -2061,7 +2114,8 @@ void *player_thread_func(void *arg) {
|
||||
}
|
||||
}
|
||||
|
||||
conn->buffer_occupancy = seq_diff(conn->ab_write, conn->ab_read); // int32_t from int16_t
|
||||
conn->buffer_occupancy =
|
||||
seq_diff(conn->ab_read, conn->ab_write, conn->ab_read); // int32_t from int32
|
||||
|
||||
if (conn->buffer_occupancy < minimum_buffer_occupancy)
|
||||
minimum_buffer_occupancy = conn->buffer_occupancy;
|
||||
@@ -2075,6 +2129,7 @@ void *player_thread_func(void *arg) {
|
||||
// If any of these are false, we don't do any synchronisation stuff
|
||||
|
||||
int resp = -1; // use this as a flag -- if negative, we can't rely on a real known delay
|
||||
current_delay = -1; // use this as a failure flag
|
||||
|
||||
if (config.output->delay) {
|
||||
long l_delay;
|
||||
@@ -2929,13 +2984,46 @@ void player_flush(uint32_t timestamp, rtsp_conn_info *conn) {
|
||||
debug(3, "player_flush");
|
||||
do_flush(timestamp, conn);
|
||||
#ifdef CONFIG_METADATA
|
||||
debug(2, "pfls");
|
||||
char numbuf[32];
|
||||
snprintf(numbuf, sizeof(numbuf), "%u", timestamp);
|
||||
send_ssnc_metadata('pfls', numbuf, strlen(numbuf), 1); // contains cancellation points
|
||||
// only send a flush metadata message if the first packet has been seen -- it's a bogus message
|
||||
// otherwise
|
||||
if (conn->first_packet_timestamp) {
|
||||
debug(2, "pfls");
|
||||
char numbuf[32];
|
||||
snprintf(numbuf, sizeof(numbuf), "%u", timestamp);
|
||||
send_ssnc_metadata('pfls', numbuf, strlen(numbuf), 1); // contains cancellation points
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void player_full_flush(rtsp_conn_info *conn) {
|
||||
// this basically flushes everything from the player
|
||||
// here, find the rtptime of the last from in the buffer and add 1 to it
|
||||
// so as to ask to flush everything
|
||||
int flush_needed = 0;
|
||||
uint32_t rtpTimestamp;
|
||||
debug_mutex_lock(&conn->ab_mutex, 30000, 0);
|
||||
if ((conn->ab_synced != 0) && (conn->ab_write != conn->ab_read)) {
|
||||
abuf_t *abuf = NULL;
|
||||
seq_t last_seqno_written;
|
||||
do {
|
||||
last_seqno_written = conn->ab_write - 1;
|
||||
abuf = conn->audio_buffer + BUFIDX(last_seqno_written);
|
||||
} while ((abuf->ready == 0) && (last_seqno_written != conn->ab_read));
|
||||
if ((abuf != NULL) && (abuf->ready != 0)) {
|
||||
debug(2, "full flush needed");
|
||||
rtpTimestamp = abuf->given_timestamp + abuf->length + 1;
|
||||
flush_needed = 1;
|
||||
} else {
|
||||
debug(2, "full flush not needed");
|
||||
}
|
||||
} else {
|
||||
debug(2, "full flush not needed -- buffers empty or not synced");
|
||||
}
|
||||
debug_mutex_unlock(&conn->ab_mutex, 0);
|
||||
if (flush_needed)
|
||||
player_flush(rtpTimestamp, conn);
|
||||
}
|
||||
|
||||
int player_play(rtsp_conn_info *conn) {
|
||||
// need to use conn in place of stream below. Need to put the stream as a parameter to he
|
||||
if (conn->player_thread != NULL)
|
||||
|
||||
@@ -341,7 +341,7 @@ typedef struct {
|
||||
uint64_t dac_buffer_queue_minimum_length;
|
||||
} rtsp_conn_info;
|
||||
|
||||
uint32_t modulo_32_offset(uint32_t from, uint32_t to);
|
||||
int32_t modulo_32_offset(uint32_t from, uint32_t to);
|
||||
|
||||
int player_play(rtsp_conn_info *conn);
|
||||
int player_stop(rtsp_conn_info *conn);
|
||||
@@ -349,7 +349,7 @@ int player_stop(rtsp_conn_info *conn);
|
||||
void player_volume(double f, rtsp_conn_info *conn);
|
||||
void player_volume_without_notification(double f, rtsp_conn_info *conn);
|
||||
void player_flush(uint32_t timestamp, rtsp_conn_info *conn);
|
||||
void player_put_packet(seq_t seqno, uint32_t actual_timestamp, uint8_t *data, int len,
|
||||
void player_put_packet(int original_format, seq_t seqno, uint32_t actual_timestamp, uint8_t *data, int len,
|
||||
rtsp_conn_info *conn);
|
||||
int64_t monotonic_timestamp(uint32_t timestamp,
|
||||
rtsp_conn_info *conn); // add an epoch to the timestamp. The monotonic
|
||||
|
||||
@@ -222,7 +222,7 @@ void *rtp_audio_receiver(void *arg) {
|
||||
if (plen >= 16) {
|
||||
if ((config.diagnostic_drop_packet_fraction == 0.0) ||
|
||||
(drand48() > config.diagnostic_drop_packet_fraction))
|
||||
player_put_packet(seqno, actual_timestamp, pktp, plen,
|
||||
player_put_packet(1, seqno, actual_timestamp, pktp, plen,
|
||||
conn); // the '1' means is original format
|
||||
else
|
||||
debug(3, "Dropping audio packet %u to simulate a bad connection.", seqno);
|
||||
@@ -469,7 +469,7 @@ void *rtp_control_receiver(void *arg) {
|
||||
|
||||
// check if packet contains enough content to be reasonable
|
||||
if (plen >= 16) {
|
||||
player_put_packet(seqno, actual_timestamp, pktp, plen,
|
||||
player_put_packet(1, seqno, actual_timestamp, pktp, plen,
|
||||
conn); // the '1' means is original format
|
||||
continue;
|
||||
} else {
|
||||
@@ -1114,140 +1114,6 @@ int local_ntp_time_to_frame(uint64_t time, uint32_t *frame, rtsp_conn_info *conn
|
||||
return result;
|
||||
}
|
||||
|
||||
void set_ptp_anchor_info(rtsp_conn_info *conn, uint64_t clock_id, uint32_t rtptime,
|
||||
uint64_t networktime) {
|
||||
if (conn->anchor_clock != clock_id)
|
||||
debug(1, "Connection %d: Set Anchor Clock: %" PRIx64 ".", conn->connection_number, clock_id);
|
||||
conn->anchor_remote_info_is_valid = 1;
|
||||
conn->anchor_rtptime = rtptime;
|
||||
conn->anchor_time = networktime;
|
||||
conn->anchor_clock = clock_id;
|
||||
}
|
||||
|
||||
void reset_ptp_anchor_info(rtsp_conn_info *conn) {
|
||||
debug(2, "Connection %d: Clear anchor information.", conn->connection_number);
|
||||
conn->last_anchor_info_is_valid = 0;
|
||||
conn->anchor_remote_info_is_valid = 0;
|
||||
}
|
||||
|
||||
int get_ptp_anchor_local_time_info(rtsp_conn_info *conn, uint32_t *anchorRTP,
|
||||
uint64_t *anchorLocalTime) {
|
||||
int response = 0;
|
||||
uint64_t actual_clock_id, actual_offset;
|
||||
|
||||
int ptp_status = ptp_get_clock_info(&actual_clock_id, &actual_offset);
|
||||
|
||||
if (ptp_status == 0) {
|
||||
if (conn->anchor_remote_info_is_valid != 0) {
|
||||
if (actual_clock_id == conn->anchor_clock) {
|
||||
conn->last_anchor_clock_offset = actual_offset;
|
||||
conn->last_anchor_time_of_update = get_absolute_time_in_ns();
|
||||
conn->last_anchor_info_is_valid = 1;
|
||||
if (anchorRTP != NULL)
|
||||
*anchorRTP = conn->anchor_rtptime;
|
||||
if (anchorLocalTime != NULL)
|
||||
*anchorLocalTime = conn->anchor_time - conn->last_anchor_clock_offset;
|
||||
} else {
|
||||
if (conn->last_anchor_info_is_valid != 0) {
|
||||
int64_t time_since_last_update =
|
||||
get_absolute_time_in_ns() - conn->last_anchor_time_of_update;
|
||||
if (time_since_last_update > 5000000000) {
|
||||
debug(1, "change master clock to %" PRIx64 ".", actual_clock_id);
|
||||
uint64_t new_anchor_time = conn->anchor_time;
|
||||
new_anchor_time = new_anchor_time - conn->last_anchor_clock_offset; // to local
|
||||
new_anchor_time = new_anchor_time + actual_offset; // to the next clock
|
||||
conn->anchor_time = new_anchor_time;
|
||||
conn->anchor_clock = actual_clock_id;
|
||||
if (anchorRTP != NULL)
|
||||
*anchorRTP = conn->anchor_rtptime;
|
||||
if (anchorLocalTime != NULL)
|
||||
*anchorLocalTime = conn->anchor_time - actual_offset;
|
||||
} else {
|
||||
if (anchorRTP != NULL)
|
||||
*anchorRTP = conn->anchor_rtptime;
|
||||
if (anchorLocalTime != NULL)
|
||||
*anchorLocalTime = conn->anchor_time - conn->last_anchor_clock_offset;
|
||||
}
|
||||
} else {
|
||||
debug(2, "no clock and no old anchor times");
|
||||
response = -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
debug(2, "don't have anchor_remote_time_info");
|
||||
response = -1;
|
||||
}
|
||||
} else if (ptp_status == -1) {
|
||||
debug(3, "don't have the ptp clock interface");
|
||||
response = -1;
|
||||
} else {
|
||||
debug(3, "ptp clock not valid");
|
||||
response = -1;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
int get_anchor_local_time_info(rtsp_conn_info *conn, uint32_t *anchorRTP,
|
||||
uint64_t *anchorLocalTime) {
|
||||
return get_ptp_anchor_local_time_info(conn, anchorRTP,anchorLocalTime);
|
||||
}
|
||||
|
||||
int have_ptp_timing_information(rtsp_conn_info *conn) {
|
||||
if (get_anchor_local_time_info(conn, NULL, NULL) == 0)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int have_timing_information(rtsp_conn_info *conn) {
|
||||
return have_ptp_timing_information(conn);
|
||||
}
|
||||
|
||||
int frame_to_ptp_local_time(uint32_t timestamp, uint64_t *time, rtsp_conn_info *conn) {
|
||||
int result = -1;
|
||||
uint32_t anchor_rtptime;
|
||||
uint64_t anchor_local_time;
|
||||
if (get_anchor_local_time_info(conn, &anchor_rtptime, &anchor_local_time) == 0) {
|
||||
int32_t frame_difference = timestamp - anchor_rtptime;
|
||||
int64_t time_difference = frame_difference;
|
||||
time_difference = time_difference * 1000000000;
|
||||
time_difference = time_difference / conn->input_rate;
|
||||
uint64_t ltime = anchor_local_time + time_difference;
|
||||
*time = ltime;
|
||||
result = 0;
|
||||
} else {
|
||||
debug(3, "frame_to_local_time can't get anchor local time information");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int frame_to_local_time(uint32_t timestamp, uint64_t *time, rtsp_conn_info *conn) {
|
||||
return frame_to_ntp_local_time(timestamp, time, conn);
|
||||
}
|
||||
|
||||
int local_ptp_time_to_frame(uint64_t time, uint32_t *frame, rtsp_conn_info *conn) {
|
||||
int result = -1;
|
||||
uint32_t anchor_rtptime;
|
||||
uint64_t anchor_local_time;
|
||||
if (get_anchor_local_time_info(conn, &anchor_rtptime, &anchor_local_time) == 0) {
|
||||
int64_t time_difference = time - anchor_local_time;
|
||||
int64_t frame_difference = time_difference;
|
||||
frame_difference = frame_difference * conn->input_rate; // but this is by 10^9
|
||||
frame_difference = frame_difference / 1000000000;
|
||||
int32_t fd32 = frame_difference;
|
||||
uint32_t lframe = anchor_rtptime + fd32;
|
||||
*frame = lframe;
|
||||
result = 0;
|
||||
} else {
|
||||
debug(3, "local_time_to_frame can't get anchor local time information");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int local_time_to_frame(uint64_t time, uint32_t *frame, rtsp_conn_info *conn) {
|
||||
return local_ntp_time_to_frame(time, frame, conn);
|
||||
}
|
||||
|
||||
void rtp_request_resend(seq_t first, uint32_t count, rtsp_conn_info *conn) {
|
||||
// debug(1, "rtp_request_resend of %u packets from sequence number %u.", count, first);
|
||||
if (conn->rtp_running) {
|
||||
@@ -1330,9 +1196,134 @@ void rtp_request_resend(seq_t first, uint32_t count, rtsp_conn_info *conn) {
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
int frame_to_local_time(uint32_t timestamp, uint64_t *time, rtsp_conn_info *conn) {
|
||||
return frame_to_ntp_local_time(timestamp, time, conn);
|
||||
}
|
||||
|
||||
int local_time_to_frame(uint64_t time, uint32_t *frame, rtsp_conn_info *conn) {
|
||||
return local_ntp_time_to_frame(time, frame, conn);
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_AIRPLAY_2
|
||||
|
||||
void set_ptp_anchor_info(rtsp_conn_info *conn, uint64_t clock_id, uint32_t rtptime,
|
||||
uint64_t networktime) {
|
||||
if (conn->anchor_clock != clock_id)
|
||||
debug(1, "Connection %d: Set Anchor Clock: %" PRIx64 ".", conn->connection_number, clock_id);
|
||||
conn->anchor_remote_info_is_valid = 1;
|
||||
conn->anchor_rtptime = rtptime;
|
||||
conn->anchor_time = networktime;
|
||||
conn->anchor_clock = clock_id;
|
||||
}
|
||||
|
||||
void reset_ptp_anchor_info(rtsp_conn_info *conn) {
|
||||
debug(2, "Connection %d: Clear anchor information.", conn->connection_number);
|
||||
conn->last_anchor_info_is_valid = 0;
|
||||
conn->anchor_remote_info_is_valid = 0;
|
||||
}
|
||||
|
||||
int get_ptp_anchor_local_time_info(rtsp_conn_info *conn, uint32_t *anchorRTP,
|
||||
uint64_t *anchorLocalTime) {
|
||||
int response = 0;
|
||||
uint64_t actual_clock_id, actual_offset;
|
||||
|
||||
int ptp_status = ptp_get_clock_info(&actual_clock_id, &actual_offset);
|
||||
|
||||
if (ptp_status == 0) {
|
||||
if (conn->anchor_remote_info_is_valid != 0) {
|
||||
if (actual_clock_id == conn->anchor_clock) {
|
||||
conn->last_anchor_clock_offset = actual_offset;
|
||||
conn->last_anchor_time_of_update = get_absolute_time_in_ns();
|
||||
conn->last_anchor_info_is_valid = 1;
|
||||
if (anchorRTP != NULL)
|
||||
*anchorRTP = conn->anchor_rtptime;
|
||||
if (anchorLocalTime != NULL)
|
||||
*anchorLocalTime = conn->anchor_time - conn->last_anchor_clock_offset;
|
||||
} else {
|
||||
if (conn->last_anchor_info_is_valid != 0) {
|
||||
int64_t time_since_last_update =
|
||||
get_absolute_time_in_ns() - conn->last_anchor_time_of_update;
|
||||
if (time_since_last_update > 5000000000) {
|
||||
debug(1, "change master clock to %" PRIx64 ".", actual_clock_id);
|
||||
uint64_t new_anchor_time = conn->anchor_time;
|
||||
new_anchor_time = new_anchor_time - conn->last_anchor_clock_offset; // to local
|
||||
new_anchor_time = new_anchor_time + actual_offset; // to the next clock
|
||||
conn->anchor_time = new_anchor_time;
|
||||
conn->anchor_clock = actual_clock_id;
|
||||
if (anchorRTP != NULL)
|
||||
*anchorRTP = conn->anchor_rtptime;
|
||||
if (anchorLocalTime != NULL)
|
||||
*anchorLocalTime = conn->anchor_time - actual_offset;
|
||||
} else {
|
||||
if (anchorRTP != NULL)
|
||||
*anchorRTP = conn->anchor_rtptime;
|
||||
if (anchorLocalTime != NULL)
|
||||
*anchorLocalTime = conn->anchor_time - conn->last_anchor_clock_offset;
|
||||
}
|
||||
} else {
|
||||
debug(2, "no clock and no old anchor times");
|
||||
response = -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
debug(2, "don't have anchor_remote_time_info");
|
||||
response = -1;
|
||||
}
|
||||
} else if (ptp_status == -1) {
|
||||
debug(3, "don't have the ptp clock interface");
|
||||
response = -1;
|
||||
} else {
|
||||
debug(3, "ptp clock not valid");
|
||||
response = -1;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
int have_ptp_timing_information(rtsp_conn_info *conn) {
|
||||
if (get_anchor_local_time_info(conn, NULL, NULL) == 0)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int frame_to_ptp_local_time(uint32_t timestamp, uint64_t *time, rtsp_conn_info *conn) {
|
||||
int result = -1;
|
||||
uint32_t anchor_rtptime;
|
||||
uint64_t anchor_local_time;
|
||||
if (get_anchor_local_time_info(conn, &anchor_rtptime, &anchor_local_time) == 0) {
|
||||
int32_t frame_difference = timestamp - anchor_rtptime;
|
||||
int64_t time_difference = frame_difference;
|
||||
time_difference = time_difference * 1000000000;
|
||||
time_difference = time_difference / conn->input_rate;
|
||||
uint64_t ltime = anchor_local_time + time_difference;
|
||||
*time = ltime;
|
||||
result = 0;
|
||||
} else {
|
||||
debug(3, "frame_to_local_time can't get anchor local time information");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int local_ptp_time_to_frame(uint64_t time, uint32_t *frame, rtsp_conn_info *conn) {
|
||||
int result = -1;
|
||||
uint32_t anchor_rtptime;
|
||||
uint64_t anchor_local_time;
|
||||
if (get_anchor_local_time_info(conn, &anchor_rtptime, &anchor_local_time) == 0) {
|
||||
int64_t time_difference = time - anchor_local_time;
|
||||
int64_t frame_difference = time_difference;
|
||||
frame_difference = frame_difference * conn->input_rate; // but this is by 10^9
|
||||
frame_difference = frame_difference / 1000000000;
|
||||
int32_t fd32 = frame_difference;
|
||||
uint32_t lframe = anchor_rtptime + fd32;
|
||||
*frame = lframe;
|
||||
result = 0;
|
||||
} else {
|
||||
debug(3, "local_time_to_frame can't get anchor local time information");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void rtp_event_receiver_cleanup_handler(void *arg) {
|
||||
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
|
||||
debug(2, "Connection %d: Event Receiver Cleanup.", conn->connection_number);
|
||||
@@ -1454,7 +1445,7 @@ int32_t decipher_player_put_packet(uint8_t *ciphered_audio_alt, ssize_t nread,
|
||||
if (new_payload_length > max_int)
|
||||
debug(1, "Madly long payload length!");
|
||||
int plen = new_payload_length; //
|
||||
player_put_packet(sequence_number, timestamp, m, plen,
|
||||
player_put_packet(1, sequence_number, timestamp, m, plen,
|
||||
conn); // the '1' means is original format
|
||||
return sequence_number;
|
||||
} else {
|
||||
@@ -2094,8 +2085,7 @@ void *rtp_buffered_audio_processor(void *arg) {
|
||||
if (streaming_has_started == 0)
|
||||
debug(1, "rtp lead time is %f ms.", 0.000001 * lead_time);
|
||||
streaming_has_started = 1;
|
||||
// player_put_packet(0, 0, pcm_buffer_read_point_rtptime,
|
||||
player_put_packet(0, pcm_buffer_read_point_rtptime,
|
||||
player_put_packet(0, 0, pcm_buffer_read_point_rtptime,
|
||||
pcm_buffer + pcm_buffer_read_point, 352, conn);
|
||||
}
|
||||
|
||||
@@ -2373,5 +2363,3 @@ void *rtp_buffered_audio_processor(void *arg) {
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user