+static size_t playrtp_callback(void *buffer,
+ size_t max_samples,
+ void attribute((unused)) *userdata) {
+ size_t samples;
+ int silent = 0;
+
+ pthread_mutex_lock(&lock);
+ /* Get the next packet, junking any that are now in the past */
+ const struct packet *p = playrtp_next_packet();
+ if(p && contains(p, next_timestamp)) {
+ /* This packet is ready to play; the desired next timestamp points
+ * somewhere into it. */
+
+ /* Timestamp of end of packet */
+ const uint32_t packet_end = p->timestamp + p->nsamples;
+
+ /* Offset of desired next timestamp into current packet */
+ const uint32_t offset = next_timestamp - p->timestamp;
+
+ /* Pointer to audio data */
+ const uint16_t *ptr = (void *)(p->samples_raw + offset);
+
+ /* Compute number of samples left in packet, limited to output buffer
+ * size */
+ samples = packet_end - next_timestamp;
+ if(samples > max_samples)
+ samples = max_samples;
+
+ /* Copy into buffer, converting to native endianness */
+ size_t i = samples;
+ int16_t *bufptr = buffer;
+ while(i > 0) {
+ *bufptr++ = (int16_t)ntohs(*ptr++);
+ --i;
+ }
+ silent = !!(p->flags & SILENT);
+ } else {
+ /* There is no suitable packet. We introduce 0s up to the next packet, or
+ * to fill the buffer if there's no next packet or that's too many. The
+ * comparison with max_samples deals with the otherwise troubling overflow
+ * case. */
+ samples = p ? p->timestamp - next_timestamp : max_samples;
+ if(samples > max_samples)
+ samples = max_samples;
+ //info("infill by %zu", samples);
+ memset(buffer, 0, samples * uaudio_sample_size);
+ silent = 1;
+ }
+ /* Debug dump */
+ if(dump_buffer) {
+ for(size_t i = 0; i < samples; ++i) {
+ dump_buffer[dump_index++] = ((int16_t *)buffer)[i];
+ dump_index %= dump_size;
+ }
+ }
+ /* Advance timestamp */
+ next_timestamp += samples;
+ /* If we're getting behind then try to drop just silent packets
+ *
+ * In theory this shouldn't be necessary. The server is supposed to send
+ * packets at the right rate and compares the number of samples sent with the
+ * time in order to ensure this.
+ *
+ * However, various things could throw this off:
+ *
+ * - the server's clock could advance at the wrong rate. This would cause it
+ * to mis-estimate the right number of samples to have sent and
+ * inappropriately throttle or speed up.
+ *
+ * - playback could happen at the wrong rate. If the playback host's sound
+ * card has a slightly incorrect clock then eventually it will get out
+ * of step.
+ *
+ * So if we play back slightly slower than the server sends for either of
+ * these reasons then eventually our buffer, and the socket's buffer, will
+ * fill, and the kernel will start dropping packets. The result is audible
+ * and not very nice.
+ *
+ * Therefore if we're getting behind, we pre-emptively drop silent packets,
+ * since a change in the duration of a silence is less noticeable than a
+ * dropped packet from the middle of continuous music.
+ *
+ * (If things go wrong the other way then eventually we run out of packets to
+ * play and are forced to play silence. This doesn't seem to happen in
+ * practice but if it does then in the same way we can artificially extend
+ * silent packets to compensate.)
+ *
+ * Dropped packets are always logged; use 'disorder-playrtp --monitor' to
+ * track how close to target buffer occupancy we are on a once-a-minute
+ * basis.
+ */
+ if(nsamples > minbuffer && silent) {
+ disorder_info("dropping %zu samples (%"PRIu32" > %"PRIu32")",
+ samples, nsamples, minbuffer);
+ samples = 0;
+ }
+ /* Junk obsolete packets */
+ playrtp_next_packet();
+ pthread_mutex_unlock(&lock);
+ return samples;
+}
+
+static int compare_family(const struct ifaddrs *a,
+ const struct ifaddrs *b,
+ int family) {
+ int afamily = a->ifa_addr->sa_family;
+ int bfamily = b->ifa_addr->sa_family;
+ if(afamily != bfamily) {
+ /* Preferred family wins */
+ if(afamily == family) return 1;
+ if(bfamily == family) return -1;
+ /* Either there's no preference or it doesn't help. Prefer IPv4 */
+ if(afamily == AF_INET) return 1;
+ if(bfamily == AF_INET) return -1;
+ /* Failing that prefer IPv6 */
+ if(afamily == AF_INET6) return 1;
+ if(bfamily == AF_INET6) return -1;
+ }
+ return 0;
+}
+
+static int compare_flags(const struct ifaddrs *a,
+ const struct ifaddrs *b) {
+ unsigned aflags = a->ifa_flags, bflags = b->ifa_flags;
+ /* Up interfaces are better than down ones */
+ unsigned aup = aflags & IFF_UP, bup = bflags & IFF_UP;
+ if(aup != bup)
+ return aup > bup ? 1 : -1;
+#if IFF_DYNAMIC
+ /* Static addresses are better than dynamic */
+ unsigned adynamic = aflags & IFF_DYNAMIC, bdynamic = bflags & IFF_DYNAMIC;
+ if(adynamic != bdynamic)
+ return adynamic < bdynamic ? 1 : -1;
+#endif
+ unsigned aloopback = aflags & IFF_LOOPBACK, bloopback = bflags & IFF_LOOPBACK;
+ /* Static addresses are better than dynamic */
+ if(aloopback != bloopback)
+ return aloopback < bloopback ? 1 : -1;
+ return 0;
+}
+
+static int compare_interfaces(const struct ifaddrs *a,
+ const struct ifaddrs *b,
+ int family) {
+ int c;
+ if((c = compare_family(a, b, family))) return c;
+ return compare_flags(a, b);
+}
+