MobAudioCapture (mob_audio_capture v0.1.0)

Copy Markdown View Source

Android only. Capture the device's whole output mix from inside a Mob app — including audio produced by other apps and by native players (a game's own AudioTrack) that bypass Mob.Audio.

A normal app cannot tap the global output mix with a session-0 Visualizer (privileged: ERROR_NO_INIT), so this plugin uses the consent-gated MediaProjection + AudioPlaybackCapture path (Android API 29+). Because capture requires a per-session system consent dialog and a typed foreground service, it is a test-environment dependency for agent-driven verification, not a capability you ship in a production app.

Platform support

  • Android 10+ (API 29): full capture of the output mix, subject to each source app's allowAudioPlaybackCapture (default-on for non-privileged apps targeting API 29+; VOICE_COMMUNICATION usage and DRM output are never captured by design).
  • iOS: not supported, and not possible. Apple provides no public API for an app to capture another app's or the system's audio output (a sandbox/privacy limit). Every call returns {:error, :unsupported_on_platform} — this is permanent, not a pending feature. To check your own app's audio instead (e.g. a game engine running inside the Mob app), meter it at the source rather than using this plugin.

Usage

MobAudioCapture.start(socket)
# → handle_info({:audio_capture, :permission, :granted | :denied}, socket)

# once granted, while audio is playing anywhere on the device:
MobAudioCapture.output_level()
# => {-12.0, -3.4}   # {rms_db, peak_db}, or :silent

MobAudioCapture.stop(socket)

Capture must be active for output_level/0 to read; otherwise it returns {:error, :not_capturing}.

Summary

Functions

Build the config map passed to audio_capture_start/1. Pure function exposed so tests can pin defaults + serialisation without the NIF.

Read the current captured-mix level as {rms_db, peak_db} (dBFS), :silent when there is no measurable signal, or {:error, reason}.

Begin capturing the device output mix. Triggers the MediaProjection consent dialog; the outcome arrives as {:audio_capture, :permission, :granted | :denied} to the calling process. Once granted, capture runs in a foreground service until stop/1.

Stop the active capture session and tear down the foreground service.

Types

level_error()

@type level_error() ::
  :unsupported_on_platform | :needs_record_audio | :not_capturing | :unknown

Functions

capture_opts(opts)

@spec capture_opts(keyword()) :: map()

Build the config map passed to audio_capture_start/1. Pure function exposed so tests can pin defaults + serialisation without the NIF.

output_level()

@spec output_level() :: {float(), float()} | :silent | {:error, level_error()}

Read the current captured-mix level as {rms_db, peak_db} (dBFS), :silent when there is no measurable signal, or {:error, reason}.

reason is :not_capturing (no active session — call start/1 first), :needs_record_audio (Android permission not granted at runtime), or :unsupported_on_platform (iOS).

start(socket, opts \\ [])

@spec start(
  Mob.Socket.t(),
  keyword()
) :: Mob.Socket.t()

Begin capturing the device output mix. Triggers the MediaProjection consent dialog; the outcome arrives as {:audio_capture, :permission, :granted | :denied} to the calling process. Once granted, capture runs in a foreground service until stop/1.

Options:

  • :usages — which audio usages to capture, any of :media, :game, :unknown (default [:media, :game, :unknown]). These are the only usages AudioPlaybackCapture is permitted to record.

stop(socket)

@spec stop(Mob.Socket.t()) :: Mob.Socket.t()

Stop the active capture session and tear down the foreground service.