Fix bugs and optimize latency for Sonos in airupnp#583
Conversation
Bug fixes: - Add missing va_end(args) in HandleRAOP (all paths) - Heap-allocate ProtocolInfo (was string literal, leaked on strdup path) - Free ProtocolInfo in DelMRDevice - Set Device->Running after ProtocolInfo assignment (prevent NULL deref) - Initialize TransitionPoll to 0 in AddMRDevice Sonos-specific optimizations: - Pre-stage SetAVTransportURI at STREAM time (was deferred to PLAY) - Auto-set HTTPLength=-3 (chunked) for Sonos devices only - Set duration to 24h instead of 1ms (prevents premature stop) - Enable drift correction by default General improvements: - Fast polling (50ms x 8 ticks) after state transitions - Reduce volume echo suppression from 1000ms to 500ms - Dynamic sample rate in PCM ProtocolInfo
| switch (event) { | ||
| case RAOP_STREAM: | ||
| // a PLAY will come later, so we'll do the load at that time | ||
| case RAOP_STREAM: { |
There was a problem hiding this comment.
I'm not 100% that RAOP_STREAM is not sent multiple times in some cases. Have you verified that?
There was a problem hiding this comment.
Looking at it again, RECORD can arrive without a prior TEARDOWN in some edge cases. I'll add a guard so setURI only fires if the device isn't already in RAOP_STREAM state.
There was a problem hiding this comment.
Be really careful with these as there are many funky cases
Inspired by swyh-rs DLNA compatibility approach: - Add DLNA.ORG_PN profile names (FLAC, WAV, LPCM) to ProtocolInfo - Change DLNA.ORG_OP from 00 to 01 (byte-seek supported) - Update DLNA.ORG_FLAGS to 01700000 (streaming-transfer + background-transfer + connection-stalling + DLNA v1.5) - Populate sampleFrequency/bitsPerSample/channels in metadata so DIDL <res> element includes audio format attributes for all codecs, not just PCM
|
Added a second commit with DLNA compatibility improvements inspired by swyh-rs:
|
| Device->MetaData.duration = 86400000; | ||
| Device->MetaData.title = "Streaming from AirConnect"; | ||
| // Sonos supports chunked transfer encoding for FLAC/WAV | ||
| if (!Device->Config.HTTPLength || Device->Config.HTTPLength == -1) Device->Config.HTTPLength = -3; |
There was a problem hiding this comment.
Are you totally sure of that. AFAIR, old Sonos devices fail with chunked on flac
There was a problem hiding this comment.
Tested on a Play:1 (S1 hardware) and chunked FLAC works fine. swyh-rs and your LMS-uPnP Sonos profile both default to chunked for FLAC/WAV too. The known problem is chunked MP3 specifically. But I've only tested on my own hardware so if you've seen it break on other models I trust your experience over mine.
There was a problem hiding this comment.
Having said that, I can't overwrite user's decision on HTTP length so I can't force it to -3 no matter what. If user wants flac, then he can add chunked.
RECORD can arrive without a prior TEARDOWN in edge cases. Skip pre-staging setURI if the device is already in RAOP_STREAM state.
Sonos buffers a fixed number of bytes before playback begins. FLAC's compression means fewer bytes per second, so the buffer takes 2-3x longer to fill compared to WAV. Switching to WAV on Sonos devices significantly reduces startup latency. Only applies when the default codec is flac and the device is detected as Sonos (via TOPOLOGY service). Users who override the codec in per-device config are unaffected.
ae7be35 to
eaa2ac3
Compare
| else if (strcasestr(Device->Config.Codec, "aac")) Device->ProtocolInfo = "http-get:*:audio/aac:DLNA.ORG_PN=AAC_ADTS;DLNA.ORG_OP=00;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=0d500000000000000000000000000000"; | ||
| else if (strcasestr(Device->Config.Codec, "mp3")) Device->ProtocolInfo = "http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=00;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=0d500000000000000000000000000000"; | ||
| else Device->ProtocolInfo = "http-get:*:audio/flac:DLNA.ORG_OP=00;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=0d500000000000000000000000000000"; | ||
| // set protocolinfo — flags modeled after swyh-rs for streaming compatibility |
There was a problem hiding this comment.
This one is a very touchy topic. I had soooo many compatibility issues with protocolinfo and various players that I can't change that.
| true, // Metadata | ||
| "", // RTP:HTTP Latency (0 = use AirPlay requested) | ||
| false, // drift | ||
| true, // drift |
There was a problem hiding this comment.
No, it is false by default and user can change it to true with -r. Such change would break compatibility with existing versions
| Device->MetaData.title = "Streaming from AirConnect"; | ||
| // Sonos supports chunked transfer encoding for FLAC/WAV | ||
| if (!Device->Config.HTTPLength || Device->Config.HTTPLength == -1) Device->Config.HTTPLength = -3; | ||
| // Sonos buffers a fixed number of BYTES before playback starts; WAV's higher |
There was a problem hiding this comment.
No you can't do that either, that would mean that user's config is overwritten, event if not silently. It is up to users to decide to use wav on their Sonos if they want lower latencies.
| } else { | ||
| Device->MetaData.remote_title = "Streaming from AirConnect"; | ||
| } | ||
| // populate audio format metadata so DIDL includes sampleFrequency/bitsPerSample |
There was a problem hiding this comment.
I'm not sure this is needed except for raw pcm
Summary
Bug fixes:
va_end(args)inHandleRAOP— called on early return and normal exitProtocolInfoviastrdup/asprintf(was string literals, inconsistent withasprintfin PCM path)ProtocolInfoinDelMRDeviceto prevent leakDevice->Running = trueafterProtocolInfoassignment to prevent NULL deref raceTransitionPollto 0 inAddMRDeviceSonos-specific optimizations:
SetAVTransportURIatRAOP_STREAMtime (was deferred toRAOP_PLAY), saving ~300msHTTPLength = -3(chunked) only for Sonos devices (detected viaTOPOLOGY_IDX)General improvements:
Test plan
Related: #582