Skip to content

Fix no audio in mirror mode on Windows (recvfrom addrlen bug) - #603

Closed
jetack wants to merge 1 commit into
FDH2:masterfrom
jetack:windows-audio-fixes
Closed

jetack wants to merge 1 commit into
FDH2:masterfrom
jetack:windows-audio-fixes

Conversation

@jetack

@jetack jetack commented Oct 13, 2026

Copy link
Copy Markdown

Problem

On Windows (MSYS2 UCRT64 build), screen mirroring from an iPad shows video
but plays no audio.

Cause

In kernel_timestamp_session_recv() (lib/raop_ntp.c), the fallback
recvfrom() passes &addrlen — the address of the int *addrlen
parameter — instead of addrlen. When the caller requests the source
address, Windows rejects the call with WSAEFAULT (10014). The RTP control
socket requests the source address until its first successful read, so it
never receives anything: no sync packets and no resent audio, and the
AAC-ELD mirror audio stays silent.

Because packetlen in raop_rtp_thread_udp() was unsigned int, the -1
return was also treated as a ~4 GB packet instead of an error (and in
debug mode it triggered assert(datalen >= 0) in utils.c).

Changes

  • lib/raop_ntp.c: pass addrlen instead of &addrlen to recvfrom()
    (both the Windows and POSIX branches).
  • lib/raop_rtp.c: make packetlen signed; skip failed control-socket
    reads (logging the socket error); don't hex-dump failed data-socket
    reads in debug mode.
  • lib/netutils.c (defensive): disable SIO_UDP_CONNRESET on Windows UDP
    sockets so ICMP port-unreachable replies don't surface as WSAECONNRESET
    on later reads.

Testing

Windows 11, MSYS2 UCRT64 (GStreamer 1.26.9), built with -DUSE_DNS_SD=1,
mirroring from an iPad: audio was silent before the fix and plays
correctly after it. Not tested on Linux or macOS.

- raop_ntp.c: the fallback recvfrom() in kernel_timestamp_session_recv()
  passed &addrlen (the address of the int* parameter) instead of addrlen.
  When a source address is requested, Windows rejects the call with
  WSAEFAULT (10014). The RTP control socket requests it until its first
  successful read, so no control packets (sync, resent audio) were ever
  received and AAC-ELD mirror audio was silent.
- raop_rtp.c: packetlen was unsigned, so a -1 return from a failed recv
  became ~4 billion and was processed as a valid packet. Make it signed;
  skip failed control-socket reads (logging the socket error), and don't
  hex-dump failed data-socket reads in debug mode.
- netutils.c (defensive): disable SIO_UDP_CONNRESET on Windows UDP
  sockets, so ICMP port-unreachable replies don't surface as
  WSAECONNRESET errors on later reads.
@orluniadev

Copy link
Copy Markdown

Confirming this on a second, independent setup: same bug, and the same fix works.

Setup: UxPlay 1.74 at ec20312, MSYS2 UCRT64 (gcc 16.2.0, GStreamer 1.28.8), -DUSE_DNS_SD=1 with Apple's Bonjour service, Windows 11 Pro, wired Ethernet with "Software Timestamp" left at its default (disabled). Client: iPad Pro (iPad16,10), AirPlay/950.7.1, screen mirroring.

Before: video fine, no audio. The first control packet (read through WSARecvMsg) gives the only rtp sync of the session; every later read in raop_rtp_thread_udp() takes the recvfrom fallback and fails. A counter in the data-socket branch showed about 900,000 reads per second and 0 frames reaching the decoder: a recvfrom() that fails with WSAEFAULT does not consume the datagram, so select() reports the socket readable again at once and the thread spins. Since packetlen is unsigned int, each -1 becomes 4294967295, passes the packetlen < 12 check and is only rejected later by raop_buffer_enqueue() (unsigned short datalen > RAOP_PACKET_LEN).

After, with a patch functionally equivalent to this PR (addrlen instead of &addrlen, failed reads skipped instead of used as a length): about 92 AAC-ELD frames/s reach the decoder, 0 failed reads, and audio plays normally on the same iPad.

I also built this PR as-is (85034dc on top of ec20312) with the toolchain above: it builds cleanly, with no -Wall warnings in the three files it touches. I have not run that exact binary with the iPad.

Minimal reproducer, no AirPlay client needed. It makes the same src_addr = NULL, addrlen = NULL call as the audio data socket:

/* gcc repro.c -o repro.exe -lws2_32 */
#include <winsock2.h>
#include <stdio.h>

int main(void) {
    WSADATA w; WSAStartup(MAKEWORD(2, 2), &w);
    SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    struct sockaddr_in a = { .sin_family = AF_INET };
    a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    bind(s, (struct sockaddr *) &a, sizeof(a));
    int len = sizeof(a); getsockname(s, (struct sockaddr *) &a, &len);
    char buf[2048], pkt[300] = {0x80, 0x60};
    sendto(s, pkt, sizeof(pkt), 0, (struct sockaddr *) &a, sizeof(a));

    int *addrlen = NULL;   /* as called from raop_rtp.c: src_addr = NULL, addrlen = NULL */
    for (int i = 0; i < 2; i++) {
        int n = recvfrom(s, buf, sizeof(buf), 0, NULL, (int *) &addrlen);   /* current code */
        printf("recvfrom(..., NULL, &addrlen) = %d, WSAGetLastError() = %d\n", n, n < 0 ? WSAGetLastError() : 0);
    }
    int n = recvfrom(s, buf, sizeof(buf), 0, NULL, addrlen);                /* fixed code */
    printf("recvfrom(..., NULL, addrlen)  = %d\n", n);
    return 0;
}
recvfrom(..., NULL, &addrlen) = -1, WSAGetLastError() = 10014
recvfrom(..., NULL, &addrlen) = -1, WSAGetLastError() = 10014
recvfrom(..., NULL, addrlen)  = 300

So the NULL, NULL calls fail too (audio data socket, and the control socket after its first packet), not only the first control-socket read described above. From reading the code, the NTP thread's replies go through the same NULL, NULL call and should be affected in the same way; I have not measured that.

This PR should also fix #605, which reports the same bug.

Thanks for UxPlay!

@fduncanh

Copy link
Copy Markdown
Collaborator

This is now fixed (hopefully) It was a recent code error that slipped in. Fix is
4c86094

PLEASE REPORT BACK IF THERE IS STILL AN ERROR!

@fduncanh fduncanh closed this Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants