Closed thiccaxe closed 5 months ago
Have you found where this AirplayServer-1 code comes from? It was dumped all at one time on Github on 1 Dec 2019. It must have some (missing) history elsewhere in some other github project, unless it was written from scratch. It's in cpp, and doesn't have any license or history, which is problematical. (I did not see any code I recognized in it). Does it have the AirPlay2 videostreaming implemented in it somewhere?
Another implementation (in C#) like this (with no front end) is this one: https://github.com/SteeBono/airplayreceiver which states an MIT license, but on inspection of the code clearly derives from (or is inspired by) the dsafa22 code that is the starting point of RPiPlay. I didn't see anything in that one that is beyond what is in UxPlay, but was not able to build it.
@thiccaxe There aren't any copyright headers with license info on any of the code files in AirplayServer-1 (except on "third-party" files) Maybe you could contact the publisher (who hopefully is the sole author too) at the published email address to find out, if you want to build something to use the AirplayServer-1 project.:
I guess you could build a front end that uses the AirplayServer-1 library, and tell users to download it and build it themselves.
Alternatively, the code might be useful to give insight into the AirPlay2 video streaming protocol, assuming it is included, without actually copying the code.
EDIT: everything turned out to be AirPlay 1
Your last comment was my plan, as I too did not see any copyrights or sources.
I think that the history of the code is also suspect; I have seen comments that discuss other AirServer-1 repositories gone missing.
However as a whole the repository is a good source for improvements to uxplay and other airplay implementation. Especially when it comes to windows, as the code seems to support it from the ground up.
@thiccaxe Please report results of your tests!
Is there a description of the library's API in there somewhere?
As this is not an issue, I'll close it, but please continue to post your findings here
The demo program is able to successfully negotiate the 'fp-setup2' handshake. Ultimately, it can get a URL for the video from an app like Nebula. it looks something like https://content.api.nebula.app/video/{author}-{title}/manifest/{id}.m3u8?token={token}. It does not hang when airplaying from youtube (from the icon built-in to youtube, not ios screen mirroring) but recieves a unusual URL "mlhls://localhost/master.m3u8". [1]. Screen Mirroring and audio sharing also work. But note that nothing is played, this data is just dumped to the console.
The implementation also supports ipv6 and ios / appletv devices seem to prefer this overall.
However, it seems as if this may not be the right choice after all. While good for the http-live streaming aspect, it seems apple has updated their alac protocol, or is atleast using new modes which the alac decoder used does not support.
At the end of the day hopefully this source code can be used as a reference to implement new features.
[1] It is unclear as to where this points. The iphone doesn't seem to open any ports, ipv6 or ipv4. mlhls protocol seems to be totally undocumented but is most likely some extension of Http live streaming. UPDATE: it seems another http request is made that doesn't seem to be totally handled by this library. It uses some "FCUP" protocol but gives a URL:
https://{subdoman}.googlevideo.com/videoplayback/id/{id}/itag/{number}/source/youtube/cpn/{videoid?}/expire/{unixtimestamp}/ei/{base64}/ip/{ipv4}/requiressl/yes/ratebypass/yes/pfa/1/goi/133/sgoap/clen{something}/hls_chunk_host/{subdoman}.googlevideo.com/mh/N_/mm/31,26/mn/ {subdomains}/ms/au,onr/mv/m/mvi/1/pl/22/force_finished/1/initcwndbps/{number}/vprv/1/playlist_type/DVR/txp/{number}/mt/{unixtimstamp?}/fvip/4/keepalive/yes/fexp/{comma seperated integer list}/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,pfa,goi,sgoap,force_finished,vprv,playlist_type/sig/{BASE64}/lsparams/hls_chunk_host,mh,mm,mn,ms,mv,mvi,pl,initcwndbps/lsig/{BASE64}/playlist/index.m3u8
- THIS WORKS WITH VLC, BUT AUDIO ONLY
Validity is 6 hours for that link.
I'm not sure "fp_setup2" does anything in AirplayServer-1....
void ap_airplay_connection::post_fp_setup2_handler(const request &req,
response &res) {
DUMP_REQUEST_WITH_CONNECTION(req);
res.with_status(ok);
}
It in fact does not seem to do anything at all. But getting to that point is difficult. I seem to have the exact same responses to the iOS client as a real Apple TV, but it never makes the fp-setup2 request. Apparently you have to switch between RTSP/1.0 and HTTP/1.1 and HTTP/1.0, make sure certain values are set in the plists, etc. (but this did not seem to help the situation)
@thiccaxe Exactly. the fp_setup2 call is a dummy call. I followed the code in src/service/ap_airplay_service.cpp comparing it to UxPlay's raop.c with raop_handlers.h included. The RTSP stuff is identical (except POST/audioMode is not present in uxplay).
The only difference is that the 32bytes sent by the client in the pair-setup set are used to initialize the pairing crypto, while in uxplay they are unused (the android "Happy Cast" app (com.hpplay.happyplay 7.1.0 APK) with unobfuscated code that dsafa22 used to create the original AirServer uses it to create the "session-id " (context) linked to the client, as AppleTV allows 16 simultaneous connections. (uxplay only allows 1). dsafa22 just says "ignore the 32bytes", and just create the crypto without them.
See https://github.com/FDH2/UxPlay/wiki/AirPlay2 which is a translation of the chinese original https://www.itread01.com/hkeyxif.html (which was also used as the basis of https://github.com/SteeBono/airplayreceiver/wiki/AirPlay2-Protocol ).
I don't see any AirPlay2 use of encryption for server-client communication after pairing (This might be fp-setup2 ?) so I guess AirplayServer-1 is still using "Legacy pairing", like uxplay.
The extra features in AirplayServer-1 are that it supports the HTTP requests listed below, and "FCUP", which are not in AirPlay. I did find the Happy Cast v7.1.0 app available on the internet, and had a link for it in the wiki, but it's gone now (maybe because this drew attention to it with the link). dsafa22 says that only v7.1.0 was the "magic" one with unobfuscated code (except for crypto) Right now I don't know if this FCUP stuff is in there but dsafa22 just didn't implement it, or it's not there. (What is FCUP for? (internet search for this produces lots of results for something unrelated to airplay....) it uses the session_id.)
(It's possible that AirplayServer-1 is derived from based on Happy Cast). dsafa didnt take any code from happycast, just analyzed it to understand the protocol..
void ap_airplay_connection::send_fcup_request(int request_id,
const std::string &url,
const std::string &session_id) {
void ap_airplay_connection::initialize_request_handlers() {
// The request route table
request_route_t routes_table[] = {
{"RTSP", "OPTIONS", "", RH(options_handler)},
{"RTSP", "POST", "/pair-setup", RH(post_pair_setup_handler)},
{"RTSP", "POST", "/pair-verify", RH(post_pair_verify_handler)},
{"RTSP", "POST", "/fp-setup", RH(post_fp_setup_handler)},
{"RTSP", "SETUP", "*", RH(setup_handler)},
{"RTSP", "GET", "/info", RH(get_info_handler)},
{"RTSP", "POST", "/feedback", RH(post_feedback_handler)},
{"RTSP", "RECORD", "*", RH(record_handler)},
{"RTSP", "GET_PARAMETER", "*", RH(get_parameter_handler)},
{"RTSP", "SET_PARAMETER", "*", RH(set_parameter_handler)},
{"RTSP", "TEARDOWN", "*", RH(teardown_handler)},
{"RTSP", "FLUSH", "*", RH(flush_handler)},
{"RTSP", "POST", "/audioMode", RH(post_audioMode)},
{"HTTP", "GET", "/server-info", RH(get_server_info_handler)},
{"HTTP", "POST", "/fp-setup", RH(post_fp_setup_handler)},
{"HTTP", "POST", "/fp-setup2", RH(post_fp_setup2_handler)},
{"HTTP", "POST", "/reverse", RH(post_reverse_handler)},
{"HTTP", "POST", "/play", RH(post_play_handler)},
{"HTTP", "POST", "/scrub", RH(post_scrub_handler)},
{"HTTP", "POST", "/rate", RH(post_rate_handler)},
{"HTTP", "POST", "/stop", RH(post_stop_handler)},
{"HTTP", "POST", "/action", RH(post_action_handler)},
{"HTTP", "GET", "/playback-info", RH(get_playback_info_handler)},
{"HTTP", "PUT", "/setProperty", RH(put_setProperty_handler)},
{"HTTP", "POST", "/getProperty", RH(post_getProperty_handler)},
};
// Register all the request handlers
for (auto route : routes_table) {
register_request_route(route);
}
OK, by searching for text strings, it appears that everything in the request route table above (as well as FCUP stuff) was implemented in the android source discovered by dsafa22 (except {"RTSP", "POST", "/audioMode", RH(post_audioMode)}
), which is an unimplemented dummy function that just returns "OK".
void ap_airplay_connection::post_audioMode(const request &req, response &res) {
DUMP_REQUEST_WITH_CONNECTION(req);
res.with_status(ok);
}
It's very possible that AirplayServer-1 is a translation of the android app with unobfuscated java code that dsafa22 found, but into c++.
It just has some features (FCUP) that dsafa22 did not implement, (dsafa22 just did AirPlay-Mirror protocol). dsfa22 did not actually take code from the android app (so the dsafa22 code is clean), just used it to discover the pieces of the mirror protocol that were needed, and documented it
Some additional requests I have seen in wireshark
PUT /setProperty?textMarkupArray HTTP/1.1
Content-Type: application/x-apple-binary-plist
Content-Length: 165
X-Apple-Device-ID: 0xaaaaaaaaaaaa
X-Apple-Session-ID: UUID
User-Agent: AirPlay/635.78.1
bplist00ÑUvalue¡Ò_2CMWritingDirectionOrthogonalLinePositionPercentage^CMTextSelector#@Y_CMTextSelectorDefaultM\e}
{
"value": [
{
"CMWritingDirectionOrthogonalLinePositionPercentage": 100,
"CMTextSelector": "CMTextSelectorDefault"
}
]
}
PUT /setProperty?selectedMediaArray HTTP/1.1
Content-Type: application/x-apple-binary-plist
Content-Length: 210
X-Apple-Device-ID: 0x84ab1a861aa6
X-Apple-Session-ID: 9e550557-2f36-4436-918a-9d89ffcd958d
User-Agent: AirPlay/635.78.1
bplist00ÑUvalue¢
Ó _MediaSelectionGroupMediaType_AutomaticallySelected_!MediaSelectionOptionsPersistentIDTsoun Ñ_MediaSelectionGroupMediaTypeTsbtl:Rv{|~
¥
{
"value": [
{
"MediaSelectionGroupMediaType": "soun",
"AutomaticallySelected": true,
"MediaSelectionOptionsPersistentID": 0
},
{
"MediaSelectionGroupMediaType": "sbtl"
}
]
}
NOTE I have seen something similar from dumped logs of AirServer (closed source):
[
"processSetProperty",
"selectedMediaArray",
[
{
"MediaSelectionGroupMediaType": "sbtl"
},
{
"MediaSelectionGroupMediaType": "soun",
"MediaSelectionOptionsPersistentID": 1
}
]
]
[
"updateSelectedMediaArray:",
[
{
"MediaSelectionGroupMediaType": "sbtl"
},
{
"MediaSelectionGroupMediaType": "soun",
"MediaSelectionOptionsPersistentID": 1
}
]
]
Also it seems the "FCUP" was somewhat recognized:
void send_fcup_request(int request_id, const std::string &url,
const std::string &session_id);
but anyway here is the dump:
POST /action HTTP/1.1
Content-Type: application/x-apple-binary-plist
Content-Length: 4870
X-Apple-Device-ID: 0xaaaaaaaaaaa
X-Apple-Session-ID: UUID
User-Agent: AirPlay/635.78.1
bplist00...
which gives us
{
"type": "unhandledURLResponse",
"params": {
"FCUP_Response_URL": "mlhls://localhost/master.m3u8",
"FCUP_Response_RequestID": 1,
"FCUP_Response_StatusCode": 0,
"FCUP_Response_Data": "..."
}
}
#EXTM3U
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MEDIA:URI="mlhls://localhost/itag/233/mediadata.m3u8",TYPE=AUDIO,GROUP-ID="233",DEFAULT=YES,AUTOSELECT=YES,NAME="Default"
#EXT-X-MEDIA:URI="mlhls://localhost/itag/234/mediadata.m3u8",TYPE=AUDIO,GROUP-ID="234",DEFAULT=YES,AUTOSELECT=YES,NAME="Default"
#EXT-X-MEDIA:URI="https://manifest.googlevideo.com/api/manifest/hls_timedtext_playlist/expire/1664767691/...",TYPE=SUBTITLES,GROUP-ID="vtt",DEFAULT=NO,AUTOSELECT=YES,NAME="English",LANGUAGE="en"
#EXT-X-STREAM-INF:BANDWIDTH=795148,CODECS="avc1.4D401E,mp4a.40.2",RESOLUTION=640x360,AUDIO="234",SUBTITLES="vtt",FRAME-RATE=30,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
mlhls://localhost/itag/230/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=342387,CODECS="avc1.4D4015,mp4a.40.5",RESOLUTION=426x240,AUDIO="233",SUBTITLES="vtt",FRAME-RATE=30,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
mlhls://localhost/itag/229/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=427037,CODECS="avc1.4D4015,mp4a.40.2",RESOLUTION=426x240,AUDIO="234",SUBTITLES="vtt",FRAME-RATE=30,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
mlhls://localhost/itag/229/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1388497,CODECS="avc1.4D401F,mp4a.40.2",RESOLUTION=854x480,AUDIO="234",SUBTITLES="vtt",FRAME-RATE=30,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
mlhls://localhost/itag/231/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=3226612,CODECS="avc1.64001F,mp4a.40.2",RESOLUTION=1280x720,AUDIO="234",SUBTITLES="vtt",FRAME-RATE=30,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
mlhls://localhost/itag/232/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=181304,CODECS="avc1.4D400C,mp4a.40.5",RESOLUTION=256x144,AUDIO="233",SUBTITLES="vtt",FRAME-RATE=30,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
mlhls://localhost/itag/269/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=5095563,CODECS="avc1.640028,mp4a.40.2",RESOLUTION=1920x1080,AUDIO="234",SUBTITLES="vtt",FRAME-RATE=30,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
mlhls://localhost/itag/270/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=97080,CODECS="vp09.00.10.08,mp4a.40.5",RESOLUTION=256x144,AUDIO="233",SUBTITLES="vtt",FRAME-RATE=15,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
mlhls://localhost/itag/602/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=159546,CODECS="vp09.00.11.08,mp4a.40.5",RESOLUTION=256x144,AUDIO="233",SUBTITLES="vtt",FRAME-RATE=30,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
mlhls://localhost/itag/603/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=272688,CODECS="vp09.00.20.08,mp4a.40.5",RESOLUTION=426x240,AUDIO="233",SUBTITLES="vtt",FRAME-RATE=30,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
mlhls://localhost/itag/604/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=357338,CODECS="vp09.00.20.08,mp4a.40.2",RESOLUTION=426x240,AUDIO="234",SUBTITLES="vtt",FRAME-RATE=30,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
mlhls://localhost/itag/604/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=548378,CODECS="vp09.00.21.08,mp4a.40.2",RESOLUTION=640x360,AUDIO="234",SUBTITLES="vtt",FRAME-RATE=30,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
mlhls://localhost/itag/605/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=895385,CODECS="vp09.00.30.08,mp4a.40.2",RESOLUTION=854x480,AUDIO="234",SUBTITLES="vtt",FRAME-RATE=30,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
mlhls://localhost/itag/606/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1665683,CODECS="vp09.00.31.08,mp4a.40.2",RESOLUTION=1280x720,AUDIO="234",SUBTITLES="vtt",FRAME-RATE=30,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
mlhls://localhost/itag/609/mediadata.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2670019,CODECS="vp09.00.40.08,mp4a.40.2",RESOLUTION=1920x1080,AUDIO="234",SUBTITLES="vtt",FRAME-RATE=30,VIDEO-RANGE=SDR,CLOSED-CAPTIONS=NONE
mlhls://localhost/itag/614/mediadata.m3u8
It seems that the mlhls://localhost
is some sort of "proxy" (?) that runs on the , which will point back to the google url.
{
"type": "unhandledURLResponse",
"params": {
"FCUP_Response_URL": "mlhls://localhost/itag/614/mediadata.m3u8",
"FCUP_Response_RequestID": 2,
"FCUP_Response_StatusCode": 0,
"FCUP_Response_Data": "..."
}
}
#EXTM3U
#YT-EXT-CONDENSED-URL:BASE-URI="https://rr3---sn-o097znz7.googlevideo.com/videoplayback/id/.../playlist/index.m3u8",PARAMS="govp,gosq",PREFIX="s/"
#EXT-X-VERSION:6
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-TARGETDURATION:6
#EXT-X-MAP:URI="https://rr3---sn-o097znz7.googlevideo.com/videoplayback/id/.../playlist/index.m3u8/govp/slices%3D0-97413/gosq/0?is=1"
#EXTINF:5.067,
s/slices%3D0-97413/0
#EXTINF:5.066,
s/slices%3D0-219,97414-1049792/1
#EXTINF:5.067,
s/slices%3D0-219,1049793-2052531/2
#EXTINF:5.067,
s/slices%3D0-219,2052532-3341820/3
#EXTINF:5.066,
s/slices%3D0-219,3341821-4763143/4
#EXTINF:5.067,
s/slices%3D0-219,4763144-6175833/5
#EXTINF:5.067,
s/slices%3D0-219,6175834-7571442/6
#EXTINF:5.066,
s/slices%3D0-219,7571443-8602069/7
#EXTINF:5.067,
s/slices%3D0-219,8602070-9656565/8
#EXTINF:5.067,
s/slices%3D0-219,9656566-10673499/9
#EXTINF:5.066,
s/slices%3D0-219,10673500-11739681/10
#EXTINF:5.067,
s/slices%3D0-219,11739682-12904070/11
#EXTINF:5.067,
s/slices%3D0-219,12904071-14109440/12
#EXTINF:5.066,
s/slices%3D0-219,14109441-15111779/13
#EXTINF:5.067,
s/slices%3D0-219,15111780-16222516/14
#EXTINF:5.067,
s/slices%3D0-219,16222517-17229491/15
#EXTINF:5.066,
s/slices%3D0-219,17229492-18757052/16
#EXTINF:5.067,
s/slices%3D0-219,18757053-20096645/17
#EXTINF:5.067,
s/slices%3D0-219,20096646-20772737/18
#EXTINF:5.066,
s/slices%3D0-219,20772738-21901761/19
#EXTINF:5.067,
s/slices%3D0-219,21901762-23223312/20
#EXTINF:5.067,
s/slices%3D0-219,23223313-24446640/21
#EXTINF:5.066,
s/slices%3D0-219,24446641-25785407/22
#EXTINF:5.067,
s/slices%3D0-219,25785408-27197934/23
#EXTINF:5.067,
s/slices%3D0-219,27197935-28546183/24
#EXTINF:5.066,
s/slices%3D0-219,28546184-29977599/25
#EXTINF:5.067,
s/slices%3D0-219,29977600-31161214/26
#EXTINF:5.067,
s/slices%3D0-219,31161215-32515779/27
#EXTINF:5.066,
s/slices%3D0-219,32515780-33640841/28
#EXTINF:5.067,
s/slices%3D0-219,33640842-34787025/29
#EXTINF:5.067,
s/slices%3D0-219,34787026-35520617/30
#EXTINF:5.066,
s/slices%3D0-219,35520618-35869650/31
#EXTINF:5.067,
s/slices%3D0-219,35869651-36227435/32
#EXTINF:2.8,
s/slices%3D0-219,36227436-36568548/33
#EXT-X-ENDLIST
Maybe https://developer.apple.com/documentation/http_live_streaming/ has more info
M3U8 What Is an M3U8 File? A file with the M3U8 file extension is a UTF-8 Encoded Audio Playlist file. They are plain text files that can be used by both audio and video players to describe where media files are located. For example, one M3U8 file may give you references to online files for an internet radio station.
M3U8 files are well-known and are implemented in the browser. But apple / YouTube is doing some hacking around to get these to work as they crash VLC and are not played in Firefox at all
do you think DMAP and/or HTTP servers are in the AirplayServer-1 code? (if so , where?; if not, external DMAP and webserver code is needed as well as renderers)
There is certainly an http server. But I doubt anyone has implemented DMAP. the protocol is not that bad, some research will need to be done on a real apple tv how it works.
Seems that /playback-info should return a duration key.
https://github.com/air-display/apsdk-public
This seems to be a new version of AirplayServer-1 and may be useful.
Seems like this is the "original"? Has a license thank god. Looks like it can be used in open source. Will need to check history a bit.
Looks like they might have airplay 2 video streaming
will check it out
there is some documentation here: https://air-display.github.io/airplay-internal/overall_architecture.html
The current air-display code is missing the fairplay/playfair implementation which is "kept private"
To see if apsdk-public works,
it would presumably not be too difficult to clone it and add playfair to provide the missing fairplay stuff.
I suspect that the author some how ripped the playfair code recently from an apple tv(and for this reason might behave differently that current os fairplay implementatoins), otherwise there would be no real reason to exclude the code. I will give it a shot though.
I'll close this again, for the moment.
when using AirplayServer-1 and uxplay, screen mirroring, fairplay uses mode 1. the apsdk public uses mode 2. Is there any importance to mode 2? Because the code from Uxplay to handle fairplay is not working.
Changing this line gets it back to "mode 1". s_instance->features_ = 0x6527FFFFF; // 0x0E5A7FFFF7 with pv // 0x0E527FFFF7 w/o pv;
The initial apsdk code appears on GitHub as an initial commit 2018-10-21.
In the plist GET/INFO response, it then called itself
name_ = "WeCast Display";
deviceID_ = "C02WF0AXJ1GQ";
model_ = "AppleTV3,2";
sourceVersion_ = "220.68";
pi_ = "b08f5a79-db29-4384-b456-a4784d9e6055";
pk_ = "99FD4299889422515FBD27949E4E1E21B2AF50A454499E3D4BE75A4E0F55FE63";
macAddress_ = deviceID_;
vv_ = 2;
features_ = 0x5A7FDFD1;
statusFlag_ = 68;
There are no copyright or License statements in that original code (except in parts with label "third party"), or anything else to indicate its provenance.
The initial commit predates dsafa22's analysis of another Android code, and doesn't seem to have any relation to that code. (based on code snippets quoted by dsafa22.
I found a link to a manual for a Wecast display device that supports both iOS and Android. https://usermanual.wiki/EC-Technology/C88040919-4304722.pdf Don't know if its related. The device is called "Wecast" not "WeCast".
EDIT: OK: "The WeCast android app is a free companion for the Wecast Media content server API."
https://play.google.com/store/apps/details?id=app.wecast.media
regardless of that situation, the reality is that we can still use improvements such as https://github.com/air-display/apsdk-public/commit/f13fe9b9c2437919bc5a7d9b6d94271f6a706c36 since those are not from the original code and are now under GPL
@thiccaxe Have you worked out how to build the demo?
The top level CMakeLists.txt seems to have an option but I didnt find out how to do it.
I suceeded in build a static libaps.a
Yes just change OFF to ON
option(BUILD_APS_DEMO "Build the demo project" ON)
I did that already
cd apsdk-public
edit CMakeLists.txt: OFF ->ON
./generate_linux_proj.sh
cd .build/linux
make
This builds target aps (libaps.a, static) What next?
cd ../../demo
cmake .
make
[ 50%] Building CXX object CMakeFiles/apsdk-demo.dir/apsdk-demo.cpp.o
apsdk-public/demo/apsdk-demo.cpp:10:10: fatal error: ap_config.h: No such file or directory
#include <ap_config.h>
^~~~~~~~~~~~~
compilation terminated.
make[2]: *** [CMakeFiles/apsdk-demo.dir/build.make:76: CMakeFiles/apsdk-demo.dir/apsdk-demo.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:83: CMakeFiles/apsdk-demo.dir/all] Error 2
make: *** [Makefile:91: all] Error 2
No run make for the root
@thiccaxe Got it! thanks
Stops at FPLY, hope you can get playfair working with it!
Yeah, like I mentioned earlier, simply copypasting uxplay code didn't do the trick. For the exact same request, UxPlay and this one respond the exact same but only UxPlay works with an iOS device. Maybe it's some weird header.
The value of "mode" seems to change between calls to fp_setup values are 0, 1, 2, 3
I saw that screen mirroring was always 2 and audio only was 3, consistently. Maybe I will need to double check
mode = data[15] (next to last byte of fairplay setup data)
data[0:3] is F, P, L, Y apsdk calls this header->signature data[4] must be 0x03 apsdk call this header-> major_version data[5] apsdk calls this header->minor_version data[6] apsdk calls this header->phase : it is 0x1 in fpsetup1, 0x3 in fpsetup2
in fpsetup1 Uxplay chooses one of 4 possible 142 byte return messages dependent on the value 0-3 of "mode"
char reply_message[4][142] = {{0x46,0x50,0x4c,0x59,0x03,0x01,0x02,0x00,0x00,0x00,0x00,0x82,0x02,0x00,0x0f,0x9f,0x3f,0x9e,0x0a,0x25,0x21,0xdb,0xdf,0x31,0x2a,0xb2,0xbf,0xb2,0x9e,0x8d,0x23,0x2b,0x63,0x76,0xa8,0xc8,0x18,0x70,0x1d,0x22,0xae,0x93,0xd8,0x27,0x37,0xfe,0xaf,0x9d,0xb4,0xfd,0xf4,0x1c,0x2d,0xba,0x9d,0x1f,0x49,0xca,0xaa,0xbf,0x65,0x91,0xac,0x1f,0x7b,0xc6,0xf7,0xe0,0x66,0x3d,0x21,0xaf,0xe0,0x15,0x65,0x95,0x3e,0xab,0x81,0xf4,0x18,0xce,0xed,0x09,0x5a,0xdb,0x7c,0x3d,0x0e,0x25,0x49,0x09,0xa7,0x98,0x31,0xd4,0x9c,0x39,0x82,0x97,0x34,0x34,0xfa,0xcb,0x42,0xc6,0x3a,0x1c,0xd9,0x11,0xa6,0xfe,0x94,0x1a,0x8a,0x6d,0x4a,0x74,0x3b,0x46,0xc3,0xa7,0x64,0x9e,0x44,0xc7,0x89,0x55,0xe4,0x9d,0x81,0x55,0x00,0x95,0x49,0xc4,0xe2,0xf7,0xa3,0xf6,0xd5,0xba},
{0x46,0x50,0x4c,0x59,0x03,0x01,0x02,0x00,0x00,0x00,0x00,0x82,0x02,0x01,0xcf,0x32,0xa2,0x57,0x14,0xb2,0x52,0x4f,0x8a,0xa0,0xad,0x7a,0xf1,0x64,0xe3,0x7b,0xcf,0x44,0x24,0xe2,0x00,0x04,0x7e,0xfc,0x0a,0xd6,0x7a,0xfc,0xd9,0x5d,0xed,0x1c,0x27,0x30,0xbb,0x59,0x1b,0x96,0x2e,0xd6,0x3a,0x9c,0x4d,0xed,0x88,0xba,0x8f,0xc7,0x8d,0xe6,0x4d,0x91,0xcc,0xfd,0x5c,0x7b,0x56,0xda,0x88,0xe3,0x1f,0x5c,0xce,0xaf,0xc7,0x43,0x19,0x95,0xa0,0x16,0x65,0xa5,0x4e,0x19,0x39,0xd2,0x5b,0x94,0xdb,0x64,0xb9,0xe4,0x5d,0x8d,0x06,0x3e,0x1e,0x6a,0xf0,0x7e,0x96,0x56,0x16,0x2b,0x0e,0xfa,0x40,0x42,0x75,0xea,0x5a,0x44,0xd9,0x59,0x1c,0x72,0x56,0xb9,0xfb,0xe6,0x51,0x38,0x98,0xb8,0x02,0x27,0x72,0x19,0x88,0x57,0x16,0x50,0x94,0x2a,0xd9,0x46,0x68,0x8a},
{0x46,0x50,0x4c,0x59,0x03,0x01,0x02,0x00,0x00,0x00,0x00,0x82,0x02,0x02,0xc1,0x69,0xa3,0x52,0xee,0xed,0x35,0xb1,0x8c,0xdd,0x9c,0x58,0xd6,0x4f,0x16,0xc1,0x51,0x9a,0x89,0xeb,0x53,0x17,0xbd,0x0d,0x43,0x36,0xcd,0x68,0xf6,0x38,0xff,0x9d,0x01,0x6a,0x5b,0x52,0xb7,0xfa,0x92,0x16,0xb2,0xb6,0x54,0x82,0xc7,0x84,0x44,0x11,0x81,0x21,0xa2,0xc7,0xfe,0xd8,0x3d,0xb7,0x11,0x9e,0x91,0x82,0xaa,0xd7,0xd1,0x8c,0x70,0x63,0xe2,0xa4,0x57,0x55,0x59,0x10,0xaf,0x9e,0x0e,0xfc,0x76,0x34,0x7d,0x16,0x40,0x43,0x80,0x7f,0x58,0x1e,0xe4,0xfb,0xe4,0x2c,0xa9,0xde,0xdc,0x1b,0x5e,0xb2,0xa3,0xaa,0x3d,0x2e,0xcd,0x59,0xe7,0xee,0xe7,0x0b,0x36,0x29,0xf2,0x2a,0xfd,0x16,0x1d,0x87,0x73,0x53,0xdd,0xb9,0x9a,0xdc,0x8e,0x07,0x00,0x6e,0x56,0xf8,0x50,0xce},
{0x46,0x50,0x4c,0x59,0x03,0x01,0x02,0x00,0x00,0x00,0x00,0x82,0x02,0x03,0x90,0x01,0xe1,0x72,0x7e,0x0f,0x57,0xf9,0xf5,0x88,0x0d,0xb1,0x04,0xa6,0x25,0x7a,0x23,0xf5,0xcf,0xff,0x1a,0xbb,0xe1,0xe9,0x30,0x45,0x25,0x1a,0xfb,0x97,0xeb,0x9f,0xc0,0x01,0x1e,0xbe,0x0f,0x3a,0x81,0xdf,0x5b,0x69,0x1d,0x76,0xac,0xb2,0xf7,0xa5,0xc7,0x08,0xe3,0xd3,0x28,0xf5,0x6b,0xb3,0x9d,0xbd,0xe5,0xf2,0x9c,0x8a,0x17,0xf4,0x81,0x48,0x7e,0x3a,0xe8,0x63,0xc6,0x78,0x32,0x54,0x22,0xe6,0xf7,0x8e,0x16,0x6d,0x18,0xaa,0x7f,0xd6,0x36,0x25,0x8b,0xce,0x28,0x72,0x6f,0x66,0x1f,0x73,0x88,0x93,0xce,0x44,0x31,0x1e,0x4b,0xe6,0xc0,0x53,0x51,0x93,0xe5,0xef,0x72,0xe8,0x68,0x62,0x33,0x72,0x9c,0x22,0x7d,0x82,0x0c,0x99,0x94,0x45,0xd8,0x92,0x46,0xc8,0xc3,0x59}};
This should be a substitute for "fairplay_setup" in third_party/fairplay/fairplay.c in
(not yet tested, though) seems to work for fpsetup-1
void fairplay_setup(const uint8_t mode, uint8_t *out) {
uint8_t reply_message[4][142] = {{0x46,0x50,0x4c,0x59,0x03,0x01,0x02,0x00,0x00,0x00,0x00,0x82,0x02,0x00,0x0f,0x9f,0x3f,0x9e,0x0a,0x25,0x21,0xdb,0xdf,0x31,0x2a,0xb2,0xbf,0xb2,0x9e,0x8d,0x23,0x2b,0x63,0x76,0xa8,0xc8,0x18,0x70,0x1d,0x22,0xae,0x93,0xd8,0x27,0x37,0xfe,0xaf,0x9d,0xb4,0xfd,0xf4,0x1c,0x2d,0xba,0x9d,0x1f,0x49,0xca,0xaa,0xbf,0x65,0x91,0xac,0x1f,0x7b,0xc6,0xf7,0xe0,0x66,0x3d,0x21,0xaf,0xe0,0x15,0x65,0x95,0x3e,0xab,0x81,0xf4,0x18,0xce,0xed,0x09,0x5a,0xdb,0x7c,0x3d,0x0e,0x25,0x49,0x09,0xa7,0x98,0x31,0xd4,0x9c,0x39,0x82,0x97,0x34,0x34,0xfa,0xcb,0x42,0xc6,0x3a,0x1c,0xd9,0x11,0xa6,0xfe,0x94,0x1a,0x8a,0x6d,0x4a,0x74,0x3b,0x46,0xc3,0xa7,0x64,0x9e,0x44,0xc7,0x89,0x55,0xe4,0x9d,0x81,0x55,0x00,0x95,0x49,0xc4,0xe2,0xf7,0xa3,0xf6,0xd5,0xba},
{0x46,0x50,0x4c,0x59,0x03,0x01,0x02,0x00,0x00,0x00,0x00,0x82,0x02,0x01,0xcf,0x32,0xa2,0x57,0x14,0xb2,0x52,0x4f,0x8a,0xa0,0xad,0x7a,0xf1,0x64,0xe3,0x7b,0xcf,0x44,0x24,0xe2,0x00,0x04,0x7e,0xfc,0x0a,0xd6,0x7a,0xfc,0xd9,0x5d,0xed,0x1c,0x27,0x30,0xbb,0x59,0x1b,0x96,0x2e,0xd6,0x3a,0x9c,0x4d,0xed,0x88,0xba,0x8f,0xc7,0x8d,0xe6,0x4d,0x91,0xcc,0xfd,0x5c,0x7b,0x56,0xda,0x88,0xe3,0x1f,0x5c,0xce,0xaf,0xc7,0x43,0x19,0x95,0xa0,0x16,0x65,0xa5,0x4e,0x19,0x39,0xd2,0x5b,0x94,0xdb,0x64,0xb9,0xe4,0x5d,0x8d,0x06,0x3e,0x1e,0x6a,0xf0,0x7e,0x96,0x56,0x16,0x2b,0x0e,0xfa,0x40,0x42,0x75,0xea,0x5a,0x44,0xd9,0x59,0x1c,0x72,0x56,0xb9,0xfb,0xe6,0x51,0x38,0x98,0xb8,0x02,0x27,0x72,0x19,0x88,0x57,0x16,0x50,0x94,0x2a,0xd9,0x46,0x68,0x8a},
{0x46,0x50,0x4c,0x59,0x03,0x01,0x02,0x00,0x00,0x00,0x00,0x82,0x02,0x02,0xc1,0x69,0xa3,0x52,0xee,0xed,0x35,0xb1,0x8c,0xdd,0x9c,0x58,0xd6,0x4f,0x16,0xc1,0x51,0x9a,0x89,0xeb,0x53,0x17,0xbd,0x0d,0x43,0x36,0xcd,0x68,0xf6,0x38,0xff,0x9d,0x01,0x6a,0x5b,0x52,0xb7,0xfa,0x92,0x16,0xb2,0xb6,0x54,0x82,0xc7,0x84,0x44,0x11,0x81,0x21,0xa2,0xc7,0xfe,0xd8,0x3d,0xb7,0x11,0x9e,0x91,0x82,0xaa,0xd7,0xd1,0x8c,0x70,0x63,0xe2,0xa4,0x57,0x55,0x59,0x10,0xaf,0x9e,0x0e,0xfc,0x76,0x34,0x7d,0x16,0x40,0x43,0x80,0x7f,0x58,0x1e,0xe4,0xfb,0xe4,0x2c,0xa9,0xde,0xdc,0x1b,0x5e,0xb2,0xa3,0xaa,0x3d,0x2e,0xcd,0x59,0xe7,0xee,0xe7,0x0b,0x36,0x29,0xf2,0x2a,0xfd,0x16,0x1d,0x87,0x73,0x53,0xdd,0xb9,0x9a,0xdc,0x8e,0x07,0x00,0x6e,0x56,0xf8,0x50,0xce},
{0x46,0x50,0x4c,0x59,0x03,0x01,0x02,0x00,0x00,0x00,0x00,0x82,0x02,0x03,0x90,0x01,0xe1,0x72,0x7e,0x0f,0x57,0xf9,0xf5,0x88,0x0d,0xb1,0x04,0xa6,0x25,0x7a,0x23,0xf5,0xcf,0xff,0x1a,0xbb,0xe1,0xe9,0x30,0x45,0x25,0x1a,0xfb,0x97,0xeb,0x9f,0xc0,0x01,0x1e,0xbe,0x0f,0x3a,0x81,0xdf,0x5b,0x69,0x1d,0x76,0xac,0xb2,0xf7,0xa5,0xc7,0x08,0xe3,0xd3,0x28,0xf5,0x6b,0xb3,0x9d,0xbd,0xe5,0xf2,0x9c,0x8a,0x17,0xf4,0x81,0x48,0x7e,0x3a,0xe8,0x63,0xc6,0x78,0x32,0x54,0x22,0xe6,0xf7,0x8e,0x16,0x6d,0x18,0xaa,0x7f,0xd6,0x36,0x25,0x8b,0xce,0x28,0x72,0x6f,0x66,0x1f,0x73,0x88,0x93,0xce,0x44,0x31,0x1e,0x4b,0xe6,0xc0,0x53,0x51,0x93,0xe5,0xef,0x72,0xe8,0x68,0x62,0x33,0x72,0x9c,0x22,0x7d,0x82,0x0c,0x99,0x94,0x45,0xd8,0x92,0x46,0xc8,0xc3,0x59}};
memcpy(out, reply_message[mode], 142);
}
I forked this to http://github.com/FDH2/apsdk-public
working in branch "test"
The first fp setup call is fine, the second fails.
The second one is now working, Just need to add fp_decrypt and playfair code.
The branch "test" will be removed. A new branch "playfair" now has the playfair implementation of fairplay integrated into apsdk-public Seems to be working. I left the third-party/playfair/fairplay.c.in unmodified and in place and added a fairplay_playfair.c.in, to replace it, and modified third-party/playfair/CMakeLists.txt to use it instead when make is run.
https://github.com/FDH2/apsdk-public/tree/playfair
Next, to explore apsdk-public.
@thiccaxe: It's all yours! From the structure I saw, I'm guessing the "private" fairplay implementation is just playfair.
Youtube app videos stream with "FCUP" stuff showing, but of course the front end is missing. Maybe the gstreamer part of UxPlay can be strapped on to it.
@fduncanh your code is not working for me! what is your ios version?
Does not work ipados 16.6 or ios 17.3
Hm, just a weird bug. Fixed in PR.
Don't see the PR (The branch is now playfair not test)
It seems that this implementation of airplay protocol (https://github.com/alexfansz/AirplayServer-1) has all of the airplay protocol implemented (and is compatible with windows). It just needs a front end.
EDIT (2024/06/24) : this code is gone now, but can be recognized as an early version of code now released https://github.com/air-display/apsdk-public
Just posting this here, if anyone from the community wants to add such a front end to this. It also has cleaned up code and what not. I am currently testing this software and may ultimately create a frontend implementation (as it is only a library)