On this page Choose a supported audio pathWi-Fi format and capture sourcesManaged: configure the sink before startImplement all four sink callbacksCore: reuse the shared audio receiverTeardown and acceptance
HARDWARE SDK / FreeRTOS

Audio integration

Choose A2DP or Wi-Fi audio, connect real playback drivers and verify capture and routing limits.

This chapter explains terminal audio hardware integration and which playback sources the APP can share on Android and iOS. Use matching SDK 1.0.0 / package revision 27 / ABI 8 headers and archives. Complete first connection first.

Choose a supported audio path

Path Model capability Terminal implementation Audio source
Bluetooth playback audio.a2dp.sink, bit15 A working A2DP Sink module, drivers and output The phone’s selected system Bluetooth media route
Wi-Fi playback audio.wifi, bit16, with network.wifi enabled A separate TLS receiver, PCM sink and actual speaker output Playback that the user authorizes and the system permits capturing
Calls calls with a real HFP/call driver Call-audio hardware and drivers See call integration

Classic Bluetooth does not guarantee A2DP Sink support. Selecting Classic Bluetooth alone must not declare audio playback. network.bluetooth.classic selects the business transport; audio.a2dp.sink describes audio receiver hardware. BLE business control can coexist with a separate Classic Bluetooth A2DP module.

The SDK intersects actual hardware, cloud authorization and capabilities understood by the client. PROFILE key36 reports hardware, authorized and negotiated audio paths. It neither indicates active playback nor changes the phone’s system route. Declare only integrated hardware; audio cannot be marked simulated. A capability checkbox, authenticated Bluetooth business session or received packet is not evidence of audible output.

Wi-Fi format and capture sources

Wi-Fi uses a separate TLS channel on TCP 5006: 16 kHz mono PCM S16LE, 160 samples / 10 ms / 320 bytes per frame. This is a voice-oriented format, not a high-fidelity music channel or an HFP replacement. Audio does not enter the video UDP queue.

One home-screen projection entry attaches Wi-Fi audio according to the selected vehicle:

Condition APP behavior
Model has no Wi-Fi audio authorization Video-only projection with no audio permission prompt; vehicle management hides audio connection and separate sharing controls
Authorized and runtime-ready Attach audio when Wi-Fi hardware / authorized / negotiated in PROFILE36 are valid, primary wifiConnected=true and PROFILE37 audioRuntime=true
Capture permission or source policy blocks samples Retain available video and report the actual capture state

Android 10+ uses system playback capture. iOS uses ReplayKit audioApp and ignores microphone audio. System permission, the audio source and its capture policy determine sample availability.

The APP does not embed TTS or generate replacement navigation speech. AMap’s public guidance callback supplies text, not original PCM; it cannot provide original navigation audio over Wi-Fi. Built-in navigation currently uses the phone’s system / A2DP route and is mutually exclusive with screen mirroring. When permitted samples are unavailable, show a source restriction or silence rather than claiming playback.

Managed: configure the sink before start

The SDK task owns audio TLS, authorization and cleanup. Implement real drivers and fill moto_audio_port from moto/audio.h. Call this after successful create and before start:

// terminal is created; all four sink callbacks use real audio drivers.
moto_result rc = moto_terminal_set_audio(terminal, &sink, NULL);
if (rc == MOTO_OK) {
    rc = moto_terminal_start(terminal);
}
// Check rc; on failure, do not enable input. Follow lifecycle cleanup rules.

A NULL third argument reuses config.network. With advanced external primary transport, explicitly supply a complete moto_network_port for the separate audio receiver. The function copies port structures; sink and network contexts must remain valid through terminal destruction.

Serialize setup on the lifecycle-owner task. Calling while running returns MOTO_BUSY; a successful stop/start retains configuration. Do not attach audio at runtime through moto_terminal_post. For independent iOS mirroring, also call moto_terminal_set_mirror(terminal, true) before starting; see video projection for prerequisites.

Implement all four sink callbacks

Callback Driver responsibility
begin(user, token) Prepare this stream; return an error if hardware is unavailable
write(user, token, pcm) Accept this stream’s PCM; copy into a bounded queue before returning for asynchronous playback
stop(user, token) Immediately invalidate queued data for this token so old audio cannot enter the next stream
status(user, token, &played, &queued) Report samples submitted to the driver and samples still queued

All four callbacks are required. They run on the SDK owner thread and must not block or reenter the SDK. PCM is borrowed until callback return; do not retain its pointer for a playback task. Identify streams by token and invalidate pending driver buffers when stopping.

MOTO_FULL drops the current live frame; MOTO_IO ends the stream. Do not retransmit stale audio or hide a slow driver with an unbounded queue. played counts samples submitted to the driver, not proven speaker output. Measure latency and quality on target hardware.

Core: reuse the shared audio receiver

Core applications call moto_audio_attach(sdk, &sink) after moto_create and before connecting. It copies the port without creating tasks or sockets; keep the sink context alive through SDK destruction.

Use moto_audio_server_create/poll/stop/destroy from moto/audio_server.h and link the package’s moto_tls component. Configuration borrows the primary SDK, network port and secure random source; these objects and callback contexts must outlive the receiver. Do not continue audio startup if creation fails.

Call moto_audio_server_poll(server, now_ms) every 10–20 ms on the same SDK owner thread using a monotonic clock. The receiver listens on 5006 only during a valid grant and owns one active connection, TLS, parsing and backpressure. Customers do not need to implement another audio parser. Stop all callers and destroy the receiver before destroying the primary SDK. For custom transport integration, follow the delivered moto_audio_wifi_* contract; never let both receivers own the same stream.

Teardown and acceptance

Stopping sharing, authorization revocation, owner changes, network loss and lease expiry must stop the old stream. After unexpected primary TCP loss, only an already delegated, still-valid mirror lease may continue under its lifecycle rules. This does not permit playback after loss of the entire network. Temporary audio credentials belong only to the current owner, session and token; never log or persist them.

On Android and iOS separately, validate permitted and blocked sources, audible speaker output, system A2DP switching, stop/restart sharing, network loss and old-buffer removal. Verify that Wi-Fi audio stops after switching to A2DP to avoid duplicate playback. Without A2DP hardware, keep route/listening acceptance pending rather than substituting negotiation results. Builds, received packets and played counters do not establish audible output. See troubleshooting.