You set an old phone up as an offline security camera, play back a night recording, and the picture is fine. The sound is not. In the quiet stretches there's a hiss that seems to swell, as if the room is breathing. When a door finally slams, it doesn't sound as loud as it should.
Usually nothing is broken. You're hearing the audio processing Android chose for the recording, and that choice starts from one value an app passes before recording begins: the audio source.
This post covers what Android's documentation says about audio sources, what automatic gain control (AGC) does to a quiet room, a small reproducible simulation with real numbers, and a test you can run on your own recordings without an internet connection.
Why does my phone security camera's audio hiss in a quiet room?
Most likely, automatic gain control is raising the level when nothing is happening. AGC is built for speech. It turns quiet input up and loud input down so a voice lands at a steady level. Point it at an empty room at night and the only thing it can turn up is the room's own noise floor: the fridge, the fan, the microphone's own self-noise. So the hiss gets louder during the quiet minutes. When a real sound arrives, AGC pulls the gain back down, and that sound ends up less dominant than it was in the room.
Whether AGC is applied, and how strongly, depends on the audio source the app records from and on the phone maker's own audio tuning. The next sections cover both.
An audio source is a use case, not a microphone
Android apps don't pick a physical microphone. They pick an AudioSource, which describes what the recording is for. Android's own audio implementation guide says this directly:
"In Android app development, a use case is referred to as an
AudioSourceinstance; and app developers request to use theAudioSourceabstraction instead of the actual audio hardware device. The Android Audio Policy Manager maps anAudioSourceinstance to a given capture path configuration (including device, gain, and pre processing) according to product-specific rules."
The important phrase is product-specific rules. The same source can sound different on a Samsung, a Xiaomi and a Pixel, because each maker decides which microphone, which gain and which preprocessing go with it. The default effects for each source live in a vendor file, /vendor/etc/audio_effects.xml.
The guide's own example config turns on AGC for the camcorder source:
<preprocess>
<stream type="voice_communication">
<apply effect="aec"/>
<apply effect="ns"/>
</stream>
<stream type="camcorder">
<apply effect="agc"/>
</stream>
</preprocess>
It also lists example tunings: a noise suppressor "tuned for wind noise suppressor for CAMCORDER", a stationary-noise suppressor for VOICE_COMMUNICATION, and AGC "tuned for far-talk for CAMCORDER".
So it would be reasonable, and fully documented, for a phone to apply far-talk AGC and wind-noise suppression to anything recorded with the camcorder source. That's a sensible choice for someone filming a birthday party. For a camera watching an empty shop at 3 a.m., it's the choice that produces the breathing hiss.
What the documentation actually guarantees per source
Here are the sources a security-camera app would realistically choose between, and what is written down about each:
| Source | What the docs say | What a camera owner should expect |
|---|---|---|
MIC |
General microphone input | Whatever the maker tuned as the default path; not specified |
CAMCORDER |
"Microphone audio source tuned for video recording, with the same orientation as the camera if available" | AGC and wind suppression are the documented example tuning; actual effects are product-specific |
VOICE_RECOGNITION |
Tuned for voice recognition; the CDD requires noise reduction and automatic gain control to be disabled by default | The only source with a written promise of no default AGC or noise reduction |
UNPROCESSED |
"Microphone audio source tuned for unprocessed (raw) sound if available, behaves like DEFAULT otherwise" (added in Android 7.0) | Raw, if the phone supports it, and you have to check |
VOICE_COMMUNICATION |
Tuned for calls; Android 10+ implementations should provide echo cancellation | Built for a two-way call, not for a room |
Two details matter here.
VOICE_RECOGNITION is the one written guarantee. Android's compatibility rules (the CDD) say devices must, by default, disable any noise reduction and any automatic gain control on this source. The AOSP guide adds a warning: turning noise suppression on by default for this source "causes the device to fail the compatibility requirement." The same section also sets tuning targets for it, including a flat frequency response (±3 dB) from 100 Hz to 4 kHz and "Effects/preprocessing disabled by default".
UNPROCESSED is optional. A phone that doesn't support it quietly behaves like DEFAULT. The CDD has devices report support through AudioManager.getProperty(PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED), which returns null when there's no support. On older and budget phones, check before relying on it.
A simulation: what AGC does to an empty room
I wanted real numbers, so I built a 60-second test file with ffmpeg: a pink-noise room floor and a single 1.5-second loud event at the 20-second mark. Then I ran it through ffmpeg's dynaudnorm filter as a stand-in for AGC.
To be clear about the limits: this isn't any phone's AGC, and it doesn't show what your phone does. dynaudnorm is a dynamic normaliser that, like AGC, raises quiet passages and holds back loud ones. It shows the shape of the effect. The test at the end of this post measures your own phone.
# Room: pink noise floor + one loud 1.5 s event at t=20 s
ffmpeg -f lavfi -i "anoisesrc=color=pink:amplitude=0.003:duration=60:sample_rate=48000:seed=7" \
-f lavfi -i "anoisesrc=color=white:amplitude=0.35:duration=1.5:sample_rate=48000:seed=3" \
-filter_complex "[1]adelay=20000|20000,apad=whole_len=2880000[e1];[0][e1]amix=inputs=2:normalize=0[m]" \
-map "[m]" -ac 1 room.wav
# AGC stand-in
ffmpeg -i room.wav -af dynaudnorm=f=500:g=31:m=30 agc.wav
Levels measured with astats (dBFS, RMS):
| Stretch | Raw room | After AGC stand-in | Change |
|---|---|---|---|
| Quiet, 0–15 s | −64.8 | −42.9 | +21.9 dB |
| Quiet, 21.5–36.5 s (just after the event) | −64.7 | −39.0 | +25.7 dB |
| Quiet, 45–60 s | −64.6 | −38.7 | +25.9 dB |
| The event, 20–21.5 s | −13.9 | −5.3 | +8.6 dB |
The quiet room went up by roughly 22 to 26 dB. The event went up by less than 9 dB. In the raw file the event stood about 51 dB above the room. After processing it stood about 33 to 37 dB above it. The event didn't get quieter. Everything else got louder.
That gap is what a sound detector depends on. I ran ffmpeg's silencedetect at −50 dB with a 2-second minimum on both files:
- Raw room: silent 0–20 s, silent 21.5–60 s. The event is isolated exactly.
- AGC stand-in: silent only for the first 2.2 s and the last 2 s. Everything from 2.2 s to 58 s counts as one long stretch of "activity", and the event can't be told apart from the room.
If you've tried to find the noises in a long overnight recording, this is part of why a fixed threshold can let you down. I wrote up the threshold side of that problem in ffmpeg's silencedetect Found Nothing at -40 dB: Finding the Noises in a Long Recording. With gain control in the chain, the room's level moves too, so no single threshold fits the whole night.
For developers: check before you choose
If you build a recorder, the platform lets you inspect, and partly control, what's applied. These are the documented calls:
// 1. Does this phone support a truly raw source?
val am = context.getSystemService(AudioManager::class.java)
val rawSupported = am.getProperty(
AudioManager.PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED
) != null
// 2. Which preprocessing effects does this phone expose at all?
val agcAvailable = AutomaticGainControl.isAvailable()
val nsAvailable = NoiseSuppressor.isAvailable()
// 3. For an AudioRecord session, explicitly turn an exposed effect off
val agc = AutomaticGainControl.create(audioRecord.audioSessionId)
agc?.enabled = false
agc?.release() // when the session ends
A few cautions, all from how the docs are worded:
-
create()can return null. An effect that isn't exposed throughaudiofxcan't be controlled through it. The CDD's control requirement is written for speech-tuned noise suppression ("MUST allow this audio effect to be controllable with theandroid.media.audiofx.NoiseSuppressorAPI"). It doesn't promise that every vendor processing step is reachable. - Your choice of source can differ from what the capture path actually does. The Audio Policy Manager maps your source to a path "according to product-specific rules." Measure on real hardware.
-
There's no universally right source.
VOICE_RECOGNITIONgives you the written no-AGC promise, but it's tuned around voice frequencies.UNPROCESSEDgives you raw sound where it's supported.CAMCORDERmatches the camera's orientation and may suit a moving, handheld recording better. For a fixed camera in a quiet room, the documented no-AGC option is the one to test first.
One more thing that silently breaks audio on unattended recorders: the microphone needs its own foreground service type. Declaring camera doesn't cover it. The details, including Android 17's change to how the failure shows up, are in Android's camera Foreground Service Type Does Not Include the Microphone.
The offline test: measure your own phone in ten minutes
You don't need the internet, an account or a cloud service for this. You need the camera phone, a laptop on the same Wi-Fi (or a USB cable), and ffmpeg.
- Record ten quiet minutes. Put the phone where it will actually live. Start recording, leave the room, and make one known sound at about the five-minute mark, like a single clap or a door closing. Then stay out.
- Copy the file off the phone over your local network or by cable.
- Measure the quiet before and after the sound:
# Quiet stretch before the clap (minute 1 to 4)
ffmpeg -ss 60 -t 180 -i night.mp4 -vn -af astats=measure_perchannel=none:measure_overall=RMS_level -f null - 2>&1 | grep "RMS level"
# Quiet stretch well after the clap (minute 6 to 9)
ffmpeg -ss 360 -t 180 -i night.mp4 -vn -af astats=measure_perchannel=none:measure_overall=RMS_level -f null - 2>&1 | grep "RMS level"
- Read the result. If both quiet stretches are within a dB or two of each other and clearly below the clap, your audio path is behaving like a plain recorder. If the quiet level drifts, or sits only a little below the clap, gain control is very likely working on the room.
- Listen to the minute right after the clap. A dip followed by hiss that slowly swells back is the "breathing" sound AGC is known for.
Run it once in the day and once at night. A fan or fridge that switches on changes the floor, and that's useful to know too.
Setup tips that help no matter which source is used
- Keep the microphone holes clear. Most phones have more than one. Check the bottom edge and near the top camera. Don't stand the phone on its bottom edge in a groove, and don't tape over a hole to stop wind.
- Don't put the phone in a closed box. A box muffles the sound and traps heat. Heat is the bigger long-term risk for a 24/7 phone, as covered in The Hardest Part of Running a Phone as a 24/7 Camera Isn't the Camera — It's Heat and the Battery.
- Move the phone away from steady noise. A fridge compressor, a ceiling fan or a router fan gives AGC something to amplify all night.
- Point the bottom microphone toward the room, not toward the wall it's mounted on.
- Check the law where you are. Many places treat recording sound more strictly than recording video. Some people turn audio off entirely for that reason.
Where Background Camera RemoteStream fits
I build Background Camera RemoteStream, an Android app that turns a spare phone into a camera that keeps recording with the screen off. Recordings stay on the phone, with no cloud account. You can watch live from any browser on the same Wi-Fi through the phone's built-in web server, and you can stream to YouTube Live when you do want an off-site copy. It's a normal install from Google Play, with no subscription and no card.
I'm not going to claim a particular audio source for it here. Every point in this post depends on the phone the app runs on, so the honest answer is to run the ten-minute test above on your own device. That works the same way for any recording app.
More on the project at superfunicular.com.
Sources
- AOSP — Configure preprocessing effects (AudioSource as use case, Audio Policy Manager "product-specific rules",
audio_effects.xml, CAMCORDER far-talk AGC / wind-noise examples, VOICE_RECOGNITION tuning and the noise-suppression warning) - Android Developers — MediaRecorder.AudioSource (CAMCORDER, UNPROCESSED, VOICE_RECOGNITION definitions)
- AOSP — Android 16 Compatibility Definition (§5.4 VOICE_RECOGNITION defaults; §5.11 unprocessed capture and
PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED) - Android Developers — AutomaticGainControl, NoiseSuppressor
- Simulation: ffmpeg 4.4.2
anoisesrc,dynaudnorm,astats,silencedetect; commands above, run 2026-10-28
Top comments (0)