playport README

The project's README from github.com/youcci/playport. As one markdown file: README.md.

PlayPort

Wireless CarPlay in your browser. A server-side CarPlay receiver that turns any screen on your network into a head unit.

License: GPL-3.0 Platform: macOS Status: experimental Tests

PlayPort live CarPlay viewer

PlayPort runs the car side of CarPlay on a Mac. Your iPhone connects wirelessly (Bluetooth bootstrap, then Wi-Fi), and every browser on the network can watch and control CarPlay — touch, rotary knob, media keys, Siri and phone calls. No dongle, no head unit, no app install: open a URL and drive.

Port notice. PlayPort's AirPlay, iAP2 and accessory-authentication code is ported from DiPlay by shihabal3amri, itself based on xcertplay by shilapi (both GPL-3.0). PlayPort replaces the Android head-unit app with a JVM server and a WebCodecs browser client. It is an independent project, not affiliated with or endorsed by the DiPlay or xcertplay authors.


Table of contents


How it works

          Bluetooth (first pairing)                     Wi-Fi / LAN
   ┌──────────────────────────────────┐   ┌─────────────────────────────────────────┐
   │  iAP2 identification             │   │  mDNS advertisement (_airplay._tcp)     │
   │  MFi authentication              │   │  AirPlay RTSP: pairing, /info, SETUP    │
   │  Wi-Fi credentials handoff       │   │  H.264 / HEVC video  +  RTP audio       │
   └──────────────────────────────────┘   │  HID input  +  iAP2 control tunnel      │
                                          └─────────────────────────────────────────┘
                        │                                     │
                        ▼                                     ▼
              ┌──────────────────────────────────────────────────────────┐
              │  PlayPort server (Kotlin/JVM + Ktor)                     │
              │  · :protocol — ported DiPlay stack (AirPlay, iAP2, MFi)  │
              │  · RTSP listener, mDNS, pairing store, BT bridge         │
              │  · decrypts media, never re-encodes it                   │
              └──────────────────────────────────────────────────────────┘
                        │  WebSocket: binary media + JSON control
                        ▼
              ┌──────────────────────────────────────────────────────────┐
              │  Browser client (TypeScript, no framework)               │
              │  · WebCodecs VideoDecoder → canvas  ·  AudioDecoder      │
              │  · pointer + keyboard → CarPlay HID                      │
              └──────────────────────────────────────────────────────────┘

The iPhone thinks it is talking to a car head unit: PlayPort advertises itself over Bonjour, authenticates with an MFi identity, receives the encrypted H.264/HEVC screen and RTP audio, and sends touch/knob/media HID reports back. The server passes the phone's encoded media straight to the browser (no server-side decoding or re-encoding), and the browser decodes it with WebCodecs.

Features

Screenshots

Display and quality Audio mixer (no active streams)
Display settings Audio mixer
Focus mode Mobile viewer
Focus mode Mobile viewer

Requirements

Component Requirement
Server macOS 13+ (developed and tested on macOS 27, Apple silicon)
Java JDK 21 (brew install openjdk@21)
Build tools Xcode Command Line Tools (xcode-select --install) for the Bluetooth bridge
Web client Node.js 20+ (only needed to build web/dist)
iPhone Any CarPlay-capable iPhone with Bluetooth and Wi-Fi
Network iPhone and Mac on the same Wi-Fi network; Bluetooth enabled on both

Quick start

git clone https://github.com/youcci/playport.git
cd playport

# 1. Add the accessory identity (see the section below) — the server refuses to start without it
mkdir -p identity/offline-mfi
cp /path/to/identity.pk8 /path/to/certificate.p7b identity/offline-mfi/

# 2. Build everything
./gradlew build                      # protocol + server, runs 96 tests
(cd web && npm install && npm run build)

# 3. Build the Bluetooth bridge (macOS RFCOMM helper)
macos/bt-bridge/build.sh

# 4. Pair the iPhone once (keep Settings > Bluetooth open on the phone)
macos/bt-bridge/bt-bridge --scan
macos/bt-bridge/bt-bridge --pair AA:BB:CC:DD:EE:FF      # confirm the code on the iPhone

# 5. Prepare macOS
#    · System Settings → General → AirDrop & Handoff → turn OFF "AirPlay Receiver"
#    · System Settings → Privacy & Security → Bluetooth → enable your terminal app
#    · Allow incoming connections for java if the firewall asks

# 6. Run the server (use the Wi-Fi name the iPhone is on)
./gradlew :server:run --args="--wireless --wifi-ssid \"YOUR_WIFI\" --wifi-passphrase \"YOUR_PASSWORD\""

Open the https:// URL the server prints (it contains an access token) and accept the self-signed certificate warning once. HTTPS is required because browsers only expose WebCodecs — the video/audio decoders — in secure contexts. On the iPhone, accept the CarPlay prompt; if it asks for a code, enter 3939. The dashboard appears in the browser as soon as the first video frame arrives.

Accessory identity (required)

The iPhone will only start CarPlay with an authenticated accessory. On every connection it requests the accessory certificate and sends a challenge; PlayPort signs it with the matching private key. AirPlay setup uses the same identity. Without these two files the server exits at startup, and there is no software workaround.

PlayPort does not ship this identity — it is third-party key material, it is not relicensed as source code, and it must not be redistributed.

Where the files go

identity/offline-mfi/identity.pk8       # PKCS#8 EC P-256 private key
identity/offline-mfi/certificate.p7b    # accessory certificate (Apple Accessories CA)

identity/ is ignored by Git. To keep it somewhere else, pass --identity-dir /path/to/identity. You can verify a pair at any time with the bundled test — it signs a challenge and checks it against the certificate:

./gradlew :server:test --tests '*MfiIdentityTest*'

Where to normally find it

  1. From a DiPlay release APK (easiest public source). DiPlay bundles an experimental identity recovered from Carlinkit firmware and documents that it is extractable by design:

    curl -L -o DiPlay.apk \
      https://github.com/shihabal3amri/DiPlay/releases/download/v0.2.7/DiPlay-0.2.7.apk
    unzip -j DiPlay.apk 'assets/offline-mfi/*' -d identity/offline-mfi
    

    See DiPlay's docs/THIRD_PARTY_NOTICES.md for provenance.

  2. From Carlinkit C2Air firmware. The pair was originally recovered from public Allwinner V821 firmware. The reverse-engineering community documents how to dump and unpack these dongles — for example ludwig-v/wireless-carplay-dongle-reverse-engineering and the projects it links. Search the unpacked root filesystem for an Apple-issued X.509 certificate and a matching PKCS#8 EC key.

  3. Your own MFi credentials. If you are an MFi licensee with an accessory identity, use your own certificate and key. A physical MFi authentication coprocessor is not supported by this project.

  4. A remote MFi server. If you run an authentication service, point PlayPort at it instead of a local identity: --mfi-server https://host --mfi-token <token>.

Caveats. The publicly available identity is shared, experimental and not issued to PlayPort. Apple can revoke it in any iOS update, and this is not an Apple-certified product. Use it for personal experimentation, do not sell it, and do not commit or redistribute the key.

Display, orientation and quality

The phone renders CarPlay at whatever resolution PlayPort advertises, so the Display panel in the browser is the quality control room:

Applying a change restarts CarPlay (the phone reconnects in a few seconds). Values persist in ~/.playport/config.json. The same knobs exist as flags for a fixed setup:

./gradlew :server:run --args="--width 1920 --height 720 --fps 60 --ui-scale 85 --hevc"

Audio and microphone

Audio is passed through per stream (media, guidance, speech, telephony) and decoded in the browser with a jitter buffer. The Audio panel has a master volume and per-stream sliders; the control dock has Call (answer/end) and Mute call, and the sidebar has Night (day/night mode). When the phone opens Siri or a call, the browser asks for microphone permission and sends audio back to the phone.

Controls

Input Action
Touch / mouse drag CarPlay touch (two contacts supported)
Arrow keys Rotary knob (up/down/left/right)
Enter Select
Esc / Backspace Back
Space Play / pause
S Siri
Viewer controls Home, Back, Select, media transport, Siri, Call, Mute call, Night, Audio, Display, Refresh video, Focus mode, Fullscreen

Use Guide for connection steps and keyboard shortcuts. Focus mode hides the header and sidebar while keeping the CarPlay controls within reach.

Configuration

Every option can be given as a CLI flag; UI changes are saved to ~/.playport/config.json (hand-editable — CLI flags always win). Run with --help for the full list.

Flag Description Default
--device-name <name> Name shown on the iPhone PlayPort
--model, --manufacturer Strings advertised to the phone playport
--airplay-port <n> AirPlay RTSP port 7000
--http-port <n> Browser UI port (HTTPS) 8080
--width, --height, --fps, --ui-scale, --hevc Display defaults 1280×720@60, 100%, H.264
--rhd Right-hand-drive layout off
--bind <address> Bind address auto-detect LAN
--identity-dir <path> Directory containing offline-mfi/ ./identity
--state-dir <path> Identity, pairings, TLS, config ~/.playport
--mfi-server, --mfi-token Remote MFi service instead of local keys —
--wireless Enable the Bluetooth/Wi-Fi bootstrap off
--wifi-ssid, --wifi-passphrase, --wifi-channel, --wifi-security Network handed to the phone auto-detect SSID
--bt-address <mac> iPhone Bluetooth address auto-discovered
--bt-bridge <path> Bluetooth helper binary macos/bt-bridge/bt-bridge
--mdns <backend> auto, dns-sd or jmdns auto (dns-sd on macOS)
--http Serve plain HTTP (WebCodecs then only works on localhost) off
--token <token> Fixed browser access token random per start

Troubleshooting

Symptom Cause and fix
bt-bridge aborts immediately (SIGABRT) macOS Bluetooth privacy. System Settings → Privacy & Security → Bluetooth → enable your terminal app.
iPhone never appears in Bluetooth settings Expected — macOS hides iPhones. Pair with macos/bt-bridge/bt-bridge --pair <address> (with Settings → Bluetooth open on the phone).
--pair hangs The device is already paired; just start the server, or use --force-pair to re-bond.
no Wi-Fi network detected macOS hides the SSID from processes without Location Services, or the Wi-Fi is on another interface. Pass --wifi-ssid and --wifi-passphrase, or grant Location Services to your terminal.
Phone connects but video is black Open the https:// URL (WebCodecs needs a secure context) and hard-refresh. Press Fix video to force a keyframe.
H.265/HEVC … cannot decode Your browser lacks HEVC. Switch the codec back to H.264 in the Display panel.
No CarPlay prompt on the iPhone Check the phone joined the same Wi-Fi. Remove stale entries in iPhone Settings → General → CarPlay, then let the supervisor retry (or restart the server).
Port 7000 already in use macOS AirPlay Receiver is on. System Settings → General → AirDrop & Handoff → turn it off.
Browser warns about the certificate Expected: the server generates a self-signed certificate in ~/.playport. Accept it once.
Stream resolution lower than requested The iPhone clamps to what its encoder supports; the header HUD shows what actually arrived.

Security and privacy

Project layout

protocol/   Kotlin/JVM library ported from DiPlay's shared module (AirPlay, iAP2, MFi, streams)
server/     Ktor server: RTSP + mDNS + Bluetooth bridge + WebSocket hub + display/mic/config APIs
web/        Vite + TypeScript browser client (WebCodecs video/audio, input, panels)
macos/      bt-bridge: Swift IOBluetooth RFCOMM helper for the wireless bootstrap
identity/   Accessory identity drop-in (gitignored — see above)
docs/       Logo and screenshots

Development

./gradlew build          # compiles both modules and runs the test suite (96 tests)
(cd web && npm run build)  # type-checks and bundles the browser client
(cd web && npm test)       # checks keyboard isolation and touch mapping
./gradlew :server:run --args="--help"

The protocol module is a faithful port of DiPlay's shared sources with Android dependencies replaced by small JVM shims (platform/Log, network/TcpLiveness, transport/IphoneUsbException), so future upstream fixes stay easy to merge. MfiIdentityTest validates the local identity against its certificate; WireTest covers the browser wire framing.

Status and limitations

PlayPort is experimental. It has been validated on macOS with an iPhone (iOS 27) over Wi-Fi: wireless bootstrap, MFi authentication, live video with touch input, display switching and persistence. Tested with H.264; HEVC passthrough is implemented but depends on your browser.

Credits and license

PlayPort is free software under the GNU General Public License v3.0 — see LICENSE. As a derivative of DiPlay and xcertplay (both GPL-3.0), it must remain under the same license: you may use, study, modify and share it, but distributed copies and modifications must keep this license and make their source available.

CarPlay, iPhone, AirPlay and MFi are trademarks of Apple Inc. PlayPort is an independent project; no Apple affiliation or endorsement is implied.

playport (youcci/playport)

Searching Database…

这是什么?PlayPort 是一个运行在 Mac 上的服务器端 CarPlay 接收器,能把你网络里的任意浏览器变成无线 CarPlay 屏幕——触控、旋钮、媒体键、Siri 和通话都包括,不需要转接器,也不用装应用。
这是要装在手机上的原生应用,还是作为服务器运行?它是你在 Mac 上运行的 JVM 服务器,扮演 CarPlay 的车机一侧;iPhone 先经蓝牙再经 Wi-Fi 连接,任意浏览器用 WebCodecs 渲染视频/音频,而不是原生车机应用。
怎么让 PlayPort 跑起来?启动服务器(先配置好配件身份),然后在浏览器里打开它打印的 URL,接受一次自签名证书警告;在 iPhone 上接受 CarPlay 提示(如果要求输入配对码,输入 3939)。
运行之前需要装好什么?一台 macOS 13+ 的 Mac(在 Apple Silicon 上测试过)、JDK 21、用于蓝牙桥接的 Xcode Command Line Tools,以及只在需要重新构建网页客户端时才要的 Node.js 20+;iPhone 只需打开蓝牙和 Wi-Fi 并在同一网络。
真正能用起来之前,最大的门槛是什么?PlayPort 需要真实的 MFi 配件证书和私钥来与 iPhone 认证,而它并不附带这份第三方密钥材料——你必须自己去弄,没有它服务器根本无法启动。
网络里的多台设备能同时把它当作 CarPlay 屏幕吗?可以——局域网里的任何浏览器(笔记本、平板、壁挂面板、另一台 Mac)都能充当查看端,多台设备可以同时连到同一个会话,不过服务器本身只能在 macOS 上运行。
我的 CarPlay 会话数据会离开这台机器吗?不会——MFi 私钥从不离开 Mac,没有任何东西被上传到别处,浏览器界面还用每次启动生成的访问令牌加自签名 HTTPS 证书锁住。
GPL-3.0 许可证和移植过来的代码是怎么回事?PlayPort 采用 GPL-3.0,其 AirPlay、iAP2 和配件认证代码移植自 GPL-3.0 项目 DiPlay 和 xcertplay,不过 PlayPort 本身是独立项目,与那些项目的作者没有隶属关系。
连接上之后,我在浏览器里实际能控制什么?最多两指的触控、键盘上的旋钮、Home/返回/选择按钮、媒体控制、Siri、接听/挂断/静音通话、日间/夜间模式,以及实时的显示/音频设置。
它支持 H.265/HEVC 视频吗?支持,如果你的浏览器能解码 HEVC(通过 VideoDecoder.isConfigSupported 检测),就可以切换过去,在相同码率下获得更清晰的画面;否则会退回 H.264,最终用哪种编解码器由手机说了算。
PlayPort 为什么不直接附带 MFi 身份,让配置更省事?这对证书/密钥是 Apple 签发给第三方的密钥材料,PlayPort 没有再分发的授权,所以你必须自己想办法弄到,例如从 DiPlay 的发布包、转储的 Carlinkit 转接器固件,或真实的 MFi 凭据。
如果 iPhone 的蓝牙/Wi-Fi 连接断了会怎样?连接监督器会检测到断开,并自动重新邀请手机,恢复蓝牙/Wi-Fi 交接,不需要手动重连。
Creator
youcci
Size
0.9 MB
Stars
287
Updated
CommitOct 01
Age
18d
Commits · 90 Days