Fix Airplay to modern AppleTV tvOS - #2899
Open
mikelambert wants to merge 1 commit into
Open
Conversation
play_url completes a clean handshake against a modern Apple TV and then no picture ever appears, with GET /playback-info returning 500. Reported in postlund#2512, and on M3U8s in postlund#149. pyatv sends POST /play, which modern receivers no longer use for video. They drive playback through a play queue over POST /command, whose body is a plist wrapping a serialized plist: { params: { data: { type: "insertPlayQueueItem", item: { "Content-Location": ..., ... } } } } setProperty and setRate use the same endpoint. GET /playback-info is not part of such a session at all, so its 500 was never a symptom of failed playback -- it was the device declining an endpoint that does not apply. Treating that 500 as fatal is what tore the session down and removed the picture. POST /command returns 500 until the sender registers a remote control session via SETUP with a type 130 stream (controlType 1, clientTypeUUID A6B27562-B43A-4F2D-B75F-82391E250194). The receiver answers with no dataPort, so nothing is connected -- requesting the channel is the entire point. This is a different channel from the MRP one in airplay/mrp_connection.py, which uses controlType 2 and a different clientTypeUUID. Since GET /playback-info cannot be polled, end of media now comes from the receiver itself: it pushes playbackState and notification messages on the event channel, which EventChannel previously acknowledged and discarded. Those messages expect no response beyond that acknowledgement. play_url therefore returns by itself again rather than running until cancelled. Video sessions must use PTP. NTP looks tempting because it is accepted -- SETUP succeeds, the remote control session registers, the receiver fetches the media and playback starts -- but the session does not survive. No events arrive on the event channel, and playback collapses after roughly twenty seconds, the receiver falling back to whatever was on screen before. Measured on tvOS 26.5: with PTP the picture is still playing after 60s and 26 events arrive over a short session; with NTP, zero events and the picture is gone by 27s despite the media having been fetched in full. Audio streaming is untouched and still uses the NTP setup in _setup_base. Two receiver behaviours worth recording, because both close the connection with no error response and neither is discoverable from the exchange: - playerLoggingID must be at most 6 characters. P/ABCD works, P/ABCDE does not, and it is length rather than case (P/PYATV also fails). - Feedback must not be in flight while the play queue commands are issued. POST /feedback is RTSP and CSeq matched, POST /command is HTTP and FIFO matched, and they share a connection. Receivers that refuse the remote control session still get POST /play and still poll, so devices predating the play queue protocol keep working. Verified against an Apple TV 4K on tvOS 26.5: Apple's reference BipBop HLS stream, which fails on the current code, plays with audio, and play_url on an 8 second clip returns after 8.5s.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
play_url completes a clean handshake against a modern Apple TV and then no picture ever appears, with GET /playback-info returning 500. Reported in #2512, and on M3U8s in #149.
pyatv sends POST /play, which modern receivers no longer use for video. They drive playback through a play queue over POST /command, whose body is a plist wrapping a serialized plist:
setProperty and setRate use the same endpoint. GET /playback-info is not part of such a session at all, so its 500 was never a symptom of failed playback -- it was the device declining an endpoint that does not apply. Treating that 500 as fatal is what tore the session down and removed the picture.
POST /command returns 500 until the sender registers a remote control session via SETUP with a type 130 stream (controlType 1, clientTypeUUID A6B27562-B43A-4F2D-B75F-82391E250194). The receiver answers with no dataPort, so nothing is connected -- requesting the channel is the entire point. This is a different channel from the MRP one in airplay/mrp_connection.py, which uses controlType 2 and a different clientTypeUUID.
Since GET /playback-info cannot be polled, end of media now comes from the receiver itself: it pushes playbackState and notification messages on the event channel, which EventChannel previously acknowledged and discarded. Those messages expect no response beyond that acknowledgement. play_url therefore returns by itself again rather than running until cancelled.
Video sessions must use PTP. NTP looks tempting because it is accepted -- SETUP succeeds, the remote control session registers, the receiver fetches the media and playback starts -- but the session does not survive. No events arrive on the event channel, and playback collapses after roughly twenty seconds, the receiver falling back to whatever was on screen before. Measured on tvOS 26.5: with PTP the picture is still playing after 60s and 26 events arrive over a short session; with NTP, zero events and the picture is gone by 27s despite the media having been fetched in full. Audio streaming is untouched and still uses the NTP setup in _setup_base.
Two receiver behaviours worth recording, because both close the connection with no error response and neither is discoverable from the exchange:
Receivers that refuse the remote control session still get POST /play and still poll, so devices predating the play queue protocol keep working.
Verified against an Apple TV 4K on tvOS 26.5: Apple's reference BipBop HLS stream, which fails on the current code, plays with audio, and play_url on an 8 second clip returns after 8.5s.